diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index 3d20de23e..b1be3d0ca 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -114,7 +114,7 @@ export default defineComponent({
 			if (this.selectMode) {
 				this.$emit('chosen', this.file);
 			} else {
-				os.modalMenu(this.getMenu(), ev.currentTarget || ev.target);
+				os.popupMenu(this.getMenu(), ev.currentTarget || ev.target);
 			}
 		},
 
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index 98f7b5482..5dadf9a11 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -629,7 +629,7 @@ export default defineComponent({
 		},
 
 		showMenu(ev) {
-			os.modalMenu(this.getMenu(), ev.currentTarget || ev.target);
+			os.popupMenu(this.getMenu(), ev.currentTarget || ev.target);
 		},
 
 		onContextmenu(ev) {
diff --git a/src/client/components/emoji-picker-dialog.vue b/src/client/components/emoji-picker-dialog.vue
index 9400819a1..5860acaa4 100644
--- a/src/client/components/emoji-picker-dialog.vue
+++ b/src/client/components/emoji-picker-dialog.vue
@@ -1,17 +1,17 @@
 <template>
-<MkModal ref="modal" :manual-showing="manualShowing" :src="src" :front="true" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
-	<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker"/>
-</MkModal>
+<MkPopup ref="popup" :manual-showing="manualShowing" :src="src" :front="true" @click="$refs.popup.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
+	<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker" style="box-shadow: 0 8px 32px rgb(0 0 0 / 30%);"/>
+</MkPopup>
 </template>
 
 <script lang="ts">
 import { defineComponent, markRaw } from 'vue';
-import MkModal from '@client/components/ui/modal.vue';
+import MkPopup from '@client/components/ui/popup.vue';
 import MkEmojiPicker from '@client/components/emoji-picker.vue';
 
 export default defineComponent({
 	components: {
-		MkModal,
+		MkPopup,
 		MkEmojiPicker,
 	},
 
@@ -33,7 +33,7 @@ export default defineComponent({
 		},
 	},
 
-	emits: ['done', 'closed'],
+	emits: ['done', 'close', 'closed'],
 
 	data() {
 		return {
@@ -44,7 +44,7 @@ export default defineComponent({
 	methods: {
 		chosen(emoji: any) {
 			this.$emit('done', emoji);
-			this.$refs.modal.close();
+			this.$refs.popup.close();
 		},
 
 		opening() {
diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue
index 6040ad378..d60105292 100644
--- a/src/client/components/note-detailed.vue
+++ b/src/client/components/note-detailed.vue
@@ -454,7 +454,7 @@ export default defineComponent({
 		renote(viaKeyboard = false) {
 			pleaseLogin();
 			this.blur();
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.renote,
 				icon: 'fas fa-retweet',
 				action: () => {
@@ -743,14 +743,14 @@ export default defineComponent({
 		},
 
 		menu(viaKeyboard = false) {
-			os.modalMenu(this.getMenu(), this.$refs.menuButton, {
+			os.popupMenu(this.getMenu(), this.$refs.menuButton, {
 				viaKeyboard
 			}).then(this.focus);
 		},
 
 		showRenoteMenu(viaKeyboard = false) {
 			if (!this.isMyRenote) return;
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.unrenote,
 				icon: 'fas fa-trash-alt',
 				danger: true,
@@ -794,7 +794,7 @@ export default defineComponent({
 
 		async clip() {
 			const clips = await os.api('clips/list');
-			os.modalMenu([{
+			os.popupMenu([{
 				icon: 'fas fa-plus',
 				text: this.$ts.createNew,
 				action: async () => {
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 504d07c0e..873b96030 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -429,7 +429,7 @@ export default defineComponent({
 		renote(viaKeyboard = false) {
 			pleaseLogin();
 			this.blur();
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.renote,
 				icon: 'fas fa-retweet',
 				action: () => {
@@ -718,14 +718,14 @@ export default defineComponent({
 		},
 
 		menu(viaKeyboard = false) {
-			os.modalMenu(this.getMenu(), this.$refs.menuButton, {
+			os.popupMenu(this.getMenu(), this.$refs.menuButton, {
 				viaKeyboard
 			}).then(this.focus);
 		},
 
 		showRenoteMenu(viaKeyboard = false) {
 			if (!this.isMyRenote) return;
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.unrenote,
 				icon: 'fas fa-trash-alt',
 				danger: true,
@@ -769,7 +769,7 @@ export default defineComponent({
 
 		async clip() {
 			const clips = await os.api('clips/list');
-			os.modalMenu([{
+			os.popupMenu([{
 				icon: 'fas fa-plus',
 				text: this.$ts.createNew,
 				action: async () => {
diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue
index 27e20fdfa..936536565 100644
--- a/src/client/components/post-form-attaches.vue
+++ b/src/client/components/post-form-attaches.vue
@@ -112,7 +112,7 @@ export default defineComponent({
 
 		showFileMenu(file, ev: MouseEvent) {
 			if (this.menu) return;
-			this.menu = os.modalMenu([{
+			this.menu = os.popupMenu([{
 				text: this.$ts.renameFile,
 				icon: 'fas fa-i-cursor',
 				action: () => { this.rename(file) }
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index ed2a934c2..969f8563a 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -664,7 +664,7 @@ export default defineComponent({
 		},
 
 		showActions(ev) {
-			os.modalMenu(postFormActions.map(action => ({
+			os.popupMenu(postFormActions.map(action => ({
 				text: action.title,
 				action: () => {
 					action.handler({
diff --git a/src/client/components/sample.vue b/src/client/components/sample.vue
index 53eac0e2e..bce02466f 100644
--- a/src/client/components/sample.vue
+++ b/src/client/components/sample.vue
@@ -93,7 +93,7 @@ export default defineComponent({
 		},
 
 		async openMenu(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				type: 'label',
 				text: 'Fruits'
 			}, {
diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue
index 8a8394382..000f6302a 100644
--- a/src/client/components/ui/button.vue
+++ b/src/client/components/ui/button.vue
@@ -115,7 +115,7 @@ export default defineComponent({
 	z-index: 1; // 他コンポーネントのbox-shadowに隠されないようにするため
 	display: block;
 	min-width: 100px;
-	width: min-content;
+	width: max-content;
 	padding: 8px 14px;
 	text-align: center;
 	font-weight: normal;
diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue
index 3e2a5fb0d..05ce5d3e1 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/ui/input.vue
@@ -210,8 +210,7 @@ export default defineComponent({
 
 	> .label {
 		font-size: 0.85em;
-		padding: 0 0 6px 6px;
-		font-weight: bold;
+		padding: 0 0 8px 12px;
 		user-select: none;
 
 		&:empty {
@@ -221,7 +220,7 @@ export default defineComponent({
 
 	> .caption {
 		font-size: 0.8em;
-		padding: 6px 0 0 6px;
+		padding: 8px 0 0 12px;
 		color: var(--fgTransparentWeak);
 
 		&:empty {
@@ -251,6 +250,7 @@ export default defineComponent({
 			outline: none;
 			box-shadow: none;
 			box-sizing: border-box;
+			transition: border-color 0.1s ease-out;
 
 			&:hover {
 				border-color: var(--inputBorderHover);
diff --git a/src/client/components/ui/modal-menu.vue b/src/client/components/ui/popup-menu.vue
similarity index 51%
rename from src/client/components/ui/modal-menu.vue
rename to src/client/components/ui/popup-menu.vue
index aac4be9c3..ceb3c47bf 100644
--- a/src/client/components/ui/modal-menu.vue
+++ b/src/client/components/ui/popup-menu.vue
@@ -1,20 +1,25 @@
 <template>
-<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
-	<MkMenu :items="items" :align="align" @close="$refs.modal.close()" class="_popup"/>
-</MkModal>
+<MkPopup ref="popup" :src="src" @closed="$emit('closed')">
+	<MkMenu :items="items" :align="align" @close="$refs.popup.close()" class="_popup" style="box-shadow: 0 8px 32px rgb(0 0 0 / 30%);"/>
+</MkPopup>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import MkModal from './modal.vue';
+import MkPopup from './popup.vue';
 import MkMenu from './menu.vue';
 
 export default defineComponent({
 	components: {
-		MkModal,
+		MkPopup,
 		MkMenu,
 	},
+
 	props: {
+		showing: {
+			type: Boolean,
+			required: true,
+		},
 		items: {
 			type: Array,
 			required: true
@@ -31,17 +36,7 @@ export default defineComponent({
 			required: false
 		},
 	},
-	emits: ['closed'],
-	computed: {
-		keymap(): any {
-			return {
-				'esc': () => this.$refs.modal.close(),
-			};
-		},
-	},
+
+	emits: ['close', 'closed'],
 });
 </script>
-
-<style lang="scss" scoped>
-
-</style>
diff --git a/src/client/components/ui/popup.vue b/src/client/components/ui/popup.vue
new file mode 100644
index 000000000..85df3f432
--- /dev/null
+++ b/src/client/components/ui/popup.vue
@@ -0,0 +1,208 @@
+<template>
+<transition :name="$store.state.animation ? 'popup-menu' : ''" :duration="$store.state.animation ? 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
+	<div v-show="manualShowing != null ? manualShowing : showing" class="ccczpooj" :class="{ front, fixed, top: position === 'top' }" ref="content" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
+		<slot></slot>
+	</div>
+</transition>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+function getFixedContainer(el: Element | null): Element | null {
+	if (el == null || el.tagName === 'BODY') return null;
+	const position = window.getComputedStyle(el).getPropertyValue('position');
+	if (position === 'fixed') {
+		return el;
+	} else {
+		return getFixedContainer(el.parentElement);
+	}
+}
+
+export default defineComponent({
+	props: {
+		manualShowing: {
+			type: Boolean,
+			required: false,
+			default: null,
+		},
+		srcCenter: {
+			type: Boolean,
+			required: false
+		},
+		src: {
+			required: false,
+		},
+		position: {
+			required: false
+		},
+		front: {
+			type: Boolean,
+			required: false,
+			default: false,
+		}
+	},
+
+	emits: ['opening', 'click', 'esc', 'close', 'closed'],
+
+	data() {
+		return {
+			showing: true,
+			fixed: false,
+			transformOrigin: 'center',
+			contentClicking: false,
+		};
+	},
+
+	mounted() {
+		this.$watch('src', () => {
+			this.fixed = getFixedContainer(this.src) != null;
+			this.$nextTick(() => {
+				this.align();
+			});
+		}, { immediate: true });
+
+		this.$nextTick(() => {
+			const popover = this.$refs.content as any;
+			new ResizeObserver((entries, observer) => {
+				this.align();
+			}).observe(popover);
+		});
+
+		document.addEventListener('mousedown', this.onDocumentClick);
+	},
+
+	beforeUnmount() {
+		document.removeEventListener('mousedown', this.onDocumentClick);
+	},
+
+	methods: {
+		align() {
+			if (this.src == null) return;
+
+			const popover = this.$refs.content as any;
+
+			if (popover == null) return;
+
+			const rect = this.src.getBoundingClientRect();
+			
+			const width = popover.offsetWidth;
+			const height = popover.offsetHeight;
+
+			let left;
+			let top;
+
+			if (this.srcCenter) {
+				const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
+				const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.src.offsetHeight / 2);
+				left = (x - (width / 2));
+				top = (y - (height / 2));
+			} else {
+				const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
+				const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.src.offsetHeight;
+				left = (x - (width / 2));
+				top = y;
+			}
+
+			if (this.fixed) {
+				if (left + width > window.innerWidth) {
+					left = window.innerWidth - width;
+				}
+
+				if (top + height > window.innerHeight) {
+					top = window.innerHeight - height;
+				}
+			} else {
+				if (left + width - window.pageXOffset > window.innerWidth) {
+					left = window.innerWidth - width + window.pageXOffset - 1;
+				}
+
+				if (top + height - window.pageYOffset > window.innerHeight) {
+					top = window.innerHeight - height + window.pageYOffset - 1;
+				}
+			}
+
+			if (top < 0) {
+				top = 0;
+			}
+
+			if (left < 0) {
+				left = 0;
+			}
+
+			if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) {
+				this.transformOrigin = 'center top';
+			} else {
+				this.transformOrigin = 'center';
+			}
+
+			popover.style.left = left + 'px';
+			popover.style.top = top + 'px';
+		},
+
+		childRendered() {
+			// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
+			const content = this.$refs.content.children[0];
+			content.addEventListener('mousedown', e => {
+				this.contentClicking = true;
+				window.addEventListener('mouseup', e => {
+					// click イベントより先に mouseup イベントが発生するかもしれないのでちょっと待つ
+					setTimeout(() => {
+						this.contentClicking = false;
+					}, 100);
+				}, { passive: true, once: true });
+			}, { passive: true });
+		},
+
+		close() {
+			this.showing = false;
+			this.$emit('close');
+		},
+
+		onClosed() {
+			this.$emit('closed');
+		},
+
+		onDocumentClick(ev) {
+			const flyoutElement = this.$refs.content;
+			let targetElement = ev.target;
+			do {
+				if (targetElement === flyoutElement) {
+					return;
+				}
+				targetElement = targetElement.parentNode;
+			} while (targetElement);
+			this.close();
+		}
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.popup-menu-enter-active {
+	transform-origin: var(--transformOrigin);
+	transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
+}
+.popup-menu-leave-active {
+	transform-origin: var(--transformOrigin);
+	transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1), transform 0.2s cubic-bezier(0.4, 0, 1, 1) !important;
+}
+.popup-menu-enter-from, .popup-menu-leave-to {
+	pointer-events: none;
+	opacity: 0;
+	transform: scale(0.9);
+}
+
+.ccczpooj {
+	position: absolute;
+	z-index: 10000;
+
+	&.fixed {
+		position: fixed;
+	}
+
+	&.front {
+		z-index: 20000;
+	}
+}
+</style>
diff --git a/src/client/components/ui/select.vue b/src/client/components/ui/select.vue
index 987d4f194..e9d43d8a6 100644
--- a/src/client/components/ui/select.vue
+++ b/src/client/components/ui/select.vue
@@ -155,8 +155,7 @@ export default defineComponent({
 
 	> .label {
 		font-size: 0.85em;
-		padding: 0 0 6px 6px;
-		font-weight: bold;
+		padding: 0 0 8px 12px;
 		user-select: none;
 
 		&:empty {
@@ -166,7 +165,7 @@ export default defineComponent({
 
 	> .caption {
 		font-size: 0.8em;
-		padding: 6px 0 0 6px;
+		padding: 8px 0 0 12px;
 		color: var(--fgTransparentWeak);
 
 		&:empty {
@@ -197,6 +196,7 @@ export default defineComponent({
 			box-shadow: none;
 			box-sizing: border-box;
 			cursor: pointer;
+			transition: border-color 0.1s ease-out;
 
 			&:hover {
 				border-color: var(--inputBorderHover);
diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue
index a61324f25..53a141f01 100644
--- a/src/client/components/ui/textarea.vue
+++ b/src/client/components/ui/textarea.vue
@@ -176,8 +176,7 @@ export default defineComponent({
 
 	> .label {
 		font-size: 0.85em;
-		padding: 0 0 6px 6px;
-		font-weight: bold;
+		padding: 0 0 8px 12px;
 		user-select: none;
 
 		&:empty {
@@ -187,7 +186,7 @@ export default defineComponent({
 
 	> .caption {
 		font-size: 0.8em;
-		padding: 6px 0 0 6px;
+		padding: 8px 0 0 12px;
 		color: var(--fgTransparentWeak);
 
 		&:empty {
@@ -218,6 +217,7 @@ export default defineComponent({
 			outline: none;
 			box-shadow: none;
 			box-sizing: border-box;
+			transition: border-color 0.1s ease-out;
 
 			&:hover {
 				border-color: var(--inputBorderHover);
diff --git a/src/client/menu.ts b/src/client/menu.ts
index 0b5341f97..8e65496cf 100644
--- a/src/client/menu.ts
+++ b/src/client/menu.ts
@@ -143,7 +143,7 @@ export const menuDef = {
 		title: 'switchUi',
 		icon: 'fas fa-columns',
 		action: (ev) => {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: i18n.locale.default,
 				action: () => {
 					localStorage.setItem('ui', 'default');
diff --git a/src/client/os.ts b/src/client/os.ts
index 987844b2d..284f982f0 100644
--- a/src/client/os.ts
+++ b/src/client/os.ts
@@ -368,10 +368,10 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
 	});
 }
 
-export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
+export function popupMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
 	return new Promise((resolve, reject) => {
 		let dispose;
-		popup(import('@client/components/ui/modal-menu.vue'), {
+		popup(import('@client/components/ui/popup-menu.vue'), {
 			items,
 			src,
 			align: options?.align,
diff --git a/src/client/pages/advanced-theme-editor.vue b/src/client/pages/advanced-theme-editor.vue
index b40d9808c..c03d88b82 100644
--- a/src/client/pages/advanced-theme-editor.vue
+++ b/src/client/pages/advanced-theme-editor.vue
@@ -272,7 +272,7 @@ export default defineComponent({
 	
 		showTypeMenu(e: MouseEvent) {
 			return new Promise<ThemeValue>((resolve) => {
-				os.modalMenu([{
+				os.popupMenu([{
 					text: this.$ts._theme.defaultValue,
 					action: () => resolve(null),
 				}, {
diff --git a/src/client/pages/clip.vue b/src/client/pages/clip.vue
index 877797555..e4b00d5e2 100644
--- a/src/client/pages/clip.vue
+++ b/src/client/pages/clip.vue
@@ -79,7 +79,7 @@ export default defineComponent({
 
 	methods: {
 		menu(ev) {
-			os.modalMenu([this.isOwned ? {
+			os.popupMenu([this.isOwned ? {
 				icon: 'fas fa-pencil-alt',
 				text: this.$ts.edit,
 				action: async () => {
diff --git a/src/client/pages/emojis.vue b/src/client/pages/emojis.vue
index 435727e19..391aff829 100644
--- a/src/client/pages/emojis.vue
+++ b/src/client/pages/emojis.vue
@@ -80,7 +80,7 @@ export default defineComponent({
 
 	methods: {
 		menu(emoji, ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				type: 'label',
 				text: ':' + emoji.name + ':',
 			}, {
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index e971ca942..7badc9da0 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -146,7 +146,7 @@ export default defineComponent({
 		},
 
 		remoteMenu(emoji, ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				type: 'label',
 				text: ':' + emoji.name + ':',
 			}, {
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue
index 90dd29d55..612bfa762 100644
--- a/src/client/pages/instance/index.vue
+++ b/src/client/pages/instance/index.vue
@@ -167,7 +167,7 @@ export default defineComponent({
 		};
 
 		const lookup = (ev) => {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: i18n.locale.user,
 				icon: 'fas fa-user',
 				action: () => {
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 1d4e816fb..1e0d4dc64 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -116,7 +116,7 @@ export default defineComponent({
 		},
 
 		start(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.messagingWithUser,
 				icon: 'fas fa-user',
 				action: () => { this.startUser() }
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index 24ed10591..b6a2fbd3d 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -320,7 +320,7 @@ const Component = defineComponent({
 		menu(ev) {
 			const path = this.groupId ? `/my/messaging/group/${this.groupId}` : `/my/messaging/${this.userAcct}`;
 
-			os.modalMenu([this.inWindow ? undefined : {
+			os.popupMenu([this.inWindow ? undefined : {
 				text: this.$ts.openInWindow,
 				icon: 'fas fa-window-maximize',
 				action: () => {
diff --git a/src/client/pages/settings/accounts.vue b/src/client/pages/settings/accounts.vue
index a3fa0d4eb..53e28bdf6 100644
--- a/src/client/pages/settings/accounts.vue
+++ b/src/client/pages/settings/accounts.vue
@@ -64,7 +64,7 @@ export default defineComponent({
 
 	methods: {
 		menu(account, ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.switch,
 				icon: 'fas fa-exchange-alt',
 				action: () => this.switchAccount(account),
@@ -77,7 +77,7 @@ export default defineComponent({
 		},
 
 		addAccount(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.existingAccount,
 				action: () => { this.addExistingAccount(); },
 			}, {
diff --git a/src/client/pages/settings/reaction.vue b/src/client/pages/settings/reaction.vue
index 9bffd5f90..a0024234e 100644
--- a/src/client/pages/settings/reaction.vue
+++ b/src/client/pages/settings/reaction.vue
@@ -94,7 +94,7 @@ export default defineComponent({
 		},
 
 		remove(reaction, ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.remove,
 				action: () => {
 					this.reactions = this.reactions.filter(x => x !== reaction)
diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue
index 55c474326..a6a0e6987 100644
--- a/src/client/pages/timeline.vue
+++ b/src/client/pages/timeline.vue
@@ -147,7 +147,7 @@ export default defineComponent({
 					this.saveSrc();
 				}
 			}));
-			os.modalMenu(items, ev.currentTarget || ev.target);
+			os.popupMenu(items, ev.currentTarget || ev.target);
 		},
 
 		async chooseAntenna(ev) {
@@ -161,7 +161,7 @@ export default defineComponent({
 					this.saveSrc();
 				}
 			}));
-			os.modalMenu(items, ev.currentTarget || ev.target);
+			os.popupMenu(items, ev.currentTarget || ev.target);
 		},
 
 		async chooseChannel(ev) {
@@ -177,7 +177,7 @@ export default defineComponent({
 					this.$router.push(`/channels/${channel.id}`);
 				}
 			}));
-			os.modalMenu(items, ev.currentTarget || ev.target);
+			os.popupMenu(items, ev.currentTarget || ev.target);
 		},
 
 		saveSrc() {
diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue
index dec5ec39e..9cf424b12 100644
--- a/src/client/pages/user/index.vue
+++ b/src/client/pages/user/index.vue
@@ -338,7 +338,7 @@ export default defineComponent({
 		},
 
 		menu(ev) {
-			os.modalMenu(getUserMenu(this.user), ev.currentTarget || ev.target);
+			os.popupMenu(getUserMenu(this.user), ev.currentTarget || ev.target);
 		},
 
 		parallaxLoop() {
diff --git a/src/client/pages/welcome.entrance.a.vue b/src/client/pages/welcome.entrance.a.vue
index da3c69426..299271c34 100644
--- a/src/client/pages/welcome.entrance.a.vue
+++ b/src/client/pages/welcome.entrance.a.vue
@@ -117,7 +117,7 @@ export default defineComponent({
 		},
 
 		showMenu(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$t('aboutX', { x: instanceName }),
 				icon: 'fas fa-info-circle',
 				action: () => {
diff --git a/src/client/pages/welcome.entrance.b.vue b/src/client/pages/welcome.entrance.b.vue
index d108eb7d9..a5c12f09e 100644
--- a/src/client/pages/welcome.entrance.b.vue
+++ b/src/client/pages/welcome.entrance.b.vue
@@ -101,7 +101,7 @@ export default defineComponent({
 		},
 
 		showMenu(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$t('aboutX', { x: instanceName }),
 				icon: 'fas fa-info-circle',
 				action: () => {
diff --git a/src/client/pages/welcome.entrance.c.vue b/src/client/pages/welcome.entrance.c.vue
index 93811e98f..2c8db6e26 100644
--- a/src/client/pages/welcome.entrance.c.vue
+++ b/src/client/pages/welcome.entrance.c.vue
@@ -121,7 +121,7 @@ export default defineComponent({
 		},
 
 		showMenu(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$t('aboutX', { x: instanceName }),
 				icon: 'fas fa-info-circle',
 				action: () => {
diff --git a/src/client/scripts/select-file.ts b/src/client/scripts/select-file.ts
index 9d7146e21..f7b971e11 100644
--- a/src/client/scripts/select-file.ts
+++ b/src/client/scripts/select-file.ts
@@ -69,7 +69,7 @@ export function selectFile(src: any, label: string | null, multiple = false) {
 			});
 		};
 
-		os.modalMenu([label ? {
+		os.popupMenu([label ? {
 			text: label,
 			type: 'label'
 		} : undefined, {
diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue
index 24b904671..67bb3abb9 100644
--- a/src/client/ui/_common_/header.vue
+++ b/src/client/ui/_common_/header.vue
@@ -31,7 +31,7 @@
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import { modalMenu } from '@client/os';
+import { popupMenu } from '@client/os';
 import { url } from '@client/config';
 
 export default defineComponent({
@@ -121,7 +121,7 @@ export default defineComponent({
 				if (menu.length > 0) menu.push(null);
 				menu = menu.concat(this.menu);
 			}
-			modalMenu(menu, ev.currentTarget || ev.target);
+			popupMenu(menu, ev.currentTarget || ev.target);
 		}
 	}
 });
diff --git a/src/client/ui/_common_/sidebar.vue b/src/client/ui/_common_/sidebar.vue
index 073907cde..ffa3a67b1 100644
--- a/src/client/ui/_common_/sidebar.vue
+++ b/src/client/ui/_common_/sidebar.vue
@@ -150,7 +150,7 @@ export default defineComponent({
 				});
 			}));
 
-			os.modalMenu([...[{
+			os.popupMenu([...[{
 				type: 'link',
 				text: this.$ts.profile,
 				to: `/@${ this.$i.username }`,
@@ -159,7 +159,7 @@ export default defineComponent({
 				icon: 'fas fa-plus',
 				text: this.$ts.addAccount,
 				action: () => {
-					os.modalMenu([{
+					os.popupMenu([{
 						text: this.$ts.existingAccount,
 						action: () => { this.addAccount(); },
 					}, {
diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue
index 7a525d9ed..6d2b9bbf5 100644
--- a/src/client/ui/chat/note.vue
+++ b/src/client/ui/chat/note.vue
@@ -432,7 +432,7 @@ export default defineComponent({
 			pleaseLogin();
 			this.operating = true;
 			this.blur();
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.renote,
 				icon: 'fas fa-retweet',
 				action: () => {
@@ -726,7 +726,7 @@ export default defineComponent({
 
 		menu(viaKeyboard = false) {
 			this.operating = true;
-			os.modalMenu(this.getMenu(), this.$refs.menuButton, {
+			os.popupMenu(this.getMenu(), this.$refs.menuButton, {
 				viaKeyboard
 			}).then(() => {
 				this.operating = false;
@@ -736,7 +736,7 @@ export default defineComponent({
 
 		showRenoteMenu(viaKeyboard = false) {
 			if (!this.isMyRenote) return;
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.unrenote,
 				icon: 'fas fa-trash-alt',
 				danger: true,
@@ -780,7 +780,7 @@ export default defineComponent({
 
 		async clip() {
 			const clips = await os.api('clips/list');
-			os.modalMenu([{
+			os.popupMenu([{
 				icon: 'fas fa-plus',
 				text: this.$ts.createNew,
 				action: async () => {
diff --git a/src/client/ui/chat/pages/channel.vue b/src/client/ui/chat/pages/channel.vue
index 76b334487..d11d40b21 100644
--- a/src/client/ui/chat/pages/channel.vue
+++ b/src/client/ui/chat/pages/channel.vue
@@ -178,7 +178,7 @@ export default defineComponent({
 		},
 
 		openChannelMenu(ev) {
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts.copyUrl,
 				icon: 'fas fa-link',
 				action: () => {
diff --git a/src/client/ui/chat/post-form.vue b/src/client/ui/chat/post-form.vue
index 6812eb31b..0f9a206fa 100644
--- a/src/client/ui/chat/post-form.vue
+++ b/src/client/ui/chat/post-form.vue
@@ -594,7 +594,7 @@ export default defineComponent({
 		},
 
 		showActions(ev) {
-			os.modalMenu(postFormActions.map(action => ({
+			os.popupMenu(postFormActions.map(action => ({
 				text: action.title,
 				action: () => {
 					action.handler({
diff --git a/src/client/ui/default.header.vue b/src/client/ui/default.header.vue
index a67883020..df2e99f13 100644
--- a/src/client/ui/default.header.vue
+++ b/src/client/ui/default.header.vue
@@ -116,7 +116,7 @@ export default defineComponent({
 				});
 			}));
 
-			os.modalMenu([...[{
+			os.popupMenu([...[{
 				type: 'link',
 				text: this.$ts.profile,
 				to: `/@${ this.$i.username }`,
@@ -125,7 +125,7 @@ export default defineComponent({
 				icon: 'fas fa-plus',
 				text: this.$ts.addAccount,
 				action: () => {
-					os.modalMenu([{
+					os.popupMenu([{
 						text: this.$ts.existingAccount,
 						action: () => { this.addAccount(); },
 					}, {
diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue
index 2e0336878..dd6d4d151 100644
--- a/src/client/ui/default.sidebar.vue
+++ b/src/client/ui/default.sidebar.vue
@@ -136,7 +136,7 @@ export default defineComponent({
 				});
 			}));
 
-			os.modalMenu([...[{
+			os.popupMenu([...[{
 				type: 'link',
 				text: this.$ts.profile,
 				to: `/@${ this.$i.username }`,
@@ -145,7 +145,7 @@ export default defineComponent({
 				icon: 'fas fa-plus',
 				text: this.$ts.addAccount,
 				action: () => {
-					os.modalMenu([{
+					os.popupMenu([{
 						text: this.$ts.existingAccount,
 						action: () => { this.addAccount(); },
 					}, {
diff --git a/src/client/widgets/timeline.vue b/src/client/widgets/timeline.vue
index 8548574af..bd951d856 100644
--- a/src/client/widgets/timeline.vue
+++ b/src/client/widgets/timeline.vue
@@ -86,7 +86,7 @@ export default defineComponent({
 					this.setSrc('list');
 				}
 			}));
-			os.modalMenu([{
+			os.popupMenu([{
 				text: this.$ts._timelines.home,
 				icon: 'fas fa-home',
 				action: () => { this.setSrc('home') }