diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3926a1748c481f2f133ee0668e45038ccbbd0187..b4aff1b6a882212a80a49aa168f5e16067cd709e 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -528,6 +528,7 @@ plugins: "プラグイン"
 pluginInstallWarn: "信頼できないプラグインはインストールしないでください。"
 deck: "デッキ"
 undeck: "デッキ解除"
+useBlurEffectForModal: "モーダルにぼかし効果を使用"
 
 _theme:
   explore: "テーマを探す"
diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue
index 58115b47a29e814505f3f9e0722c8d00648e8c9d..0ca9a5c69e8a0730b4dd1cb2784f86426157f8f0 100644
--- a/src/client/components/dialog.vue
+++ b/src/client/components/dialog.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mk-dialog" :class="{ iconOnly }">
 	<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
-		<div class="bg" ref="bg" @click="onBgClick" v-if="show"></div>
+		<div class="bg _modalBg" ref="bg" @click="onBgClick" v-if="show"></div>
 	</transition>
 	<transition :name="$store.state.device.animation ? 'dialog' : ''" appear @after-leave="() => { destroyDom(); }">
 		<div class="main" ref="main" v-if="show">
@@ -245,16 +245,6 @@ export default Vue.extend({
 		width: initial;
 	}
 
-	> .bg {
-		display: block;
-		position: fixed;
-		top: 0;
-		left: 0;
-		width: 100%;
-		height: 100%;
-		background: rgba(0,0,0,0.7);
-	}
-
 	> .main {
 		display: block;
 		position: fixed;
diff --git a/src/client/components/modal.vue b/src/client/components/modal.vue
index f941d4d5032aad208af87b18ec5da0b30f340371..6ae46d451e9cc5857f45868bb9803cc710ed1d97 100644
--- a/src/client/components/modal.vue
+++ b/src/client/components/modal.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mk-modal" v-hotkey.global="keymap">
 	<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
-		<div class="bg" ref="bg" v-if="show" @click="canClose ? close() : () => {}"></div>
+		<div class="bg _modalBg" ref="bg" v-if="show" @click="canClose ? close() : () => {}"></div>
 	</transition>
 	<transition :name="$store.state.device.animation ? 'modal' : ''" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
 		<div class="content" ref="content" v-if="show" @click.self="canClose ? close() : () => {}"><slot></slot></div>
@@ -60,13 +60,7 @@ export default Vue.extend({
 
 .mk-modal {
 	> .bg {
-		position: fixed;
-		top: 0;
-		left: 0;
 		z-index: 10000;
-		width: 100%;
-		height: 100%;
-		background: var(--modalBg)
 	}
 
 	> .content {
diff --git a/src/client/components/popup.vue b/src/client/components/popup.vue
index 0af83162e453d37425c3c55b2bff52d6db8705a0..6d27ff7ae0505289f377551fba7b5347176e439e 100644
--- a/src/client/components/popup.vue
+++ b/src/client/components/popup.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mk-popup" v-hotkey.global="keymap">
 	<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
-		<div class="bg" ref="bg" @click="close()" v-if="show"></div>
+		<div class="bg _modalBg" ref="bg" @click="close()" v-if="show"></div>
 	</transition>
 	<transition :name="$store.state.device.animation ? 'popup' : ''" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
 		<div class="content" :class="{ fixed }" ref="content" v-if="show" :style="{ width: width ? width + 'px' : 'auto' }"><slot></slot></div>
@@ -128,13 +128,7 @@ export default Vue.extend({
 
 .mk-popup {
 	> .bg {
-		position: fixed;
-		top: 0;
-		left: 0;
 		z-index: 10000;
-		width: 100%;
-		height: 100%;
-		background: var(--modalBg)
 	}
 
 	> .content {
diff --git a/src/client/components/post-form-dialog.vue b/src/client/components/post-form-dialog.vue
index 9cb527af23582d5a96fae66b774ab7297368e1aa..14227fec5deadac52e5cdfa0ec55288f5aae8e4c 100644
--- a/src/client/components/post-form-dialog.vue
+++ b/src/client/components/post-form-dialog.vue
@@ -1,7 +1,7 @@
 <template>
-<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
+<div class="ulveipgl">
 	<transition :name="$store.state.device.animation ? 'form-fade' : ''" appear @after-leave="$emit('closed');">
-		<div class="bg" ref="bg" v-if="show" @click="close()"></div>
+		<div class="bg _modalBg" ref="bg" v-if="show" @click="close()"></div>
 	</transition>
 	<div class="main" ref="main" @click.self="close()" @keydown="onKeydown">
 		<transition :name="$store.state.device.animation ? 'form' : ''" appear
@@ -119,16 +119,9 @@ export default Vue.extend({
 	opacity: 0;
 }
 
-.ulveipglmagnxfgvitaxyszerjwiqmwl {
+.ulveipgl {
 	> .bg {
-		display: block;
-		position: fixed;
 		z-index: 10000;
-		top: 0;
-		left: 0;
-		width: 100%;
-		height: 100%;
-		background: rgba(#000, 0.7);
 	}
 
 	> .main {
diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue
index 975dccb04d730a8e2a4b5cf1449d61e9832857a5..e926b4447ea68af4e5d1869625c8543de8f14d73 100644
--- a/src/client/components/sidebar.vue
+++ b/src/client/components/sidebar.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mvcprjjd">
 	<transition name="nav-back">
-		<div class="nav-back"
+		<div class="nav-back _modalBg"
 			v-if="showing"
 			@click="showing = false"
 			@touchstart="showing = false"
@@ -320,13 +320,7 @@ export default Vue.extend({
 	$nav-hide-threshold: 650px; // TODO: どこかに集約したい
 
 	> .nav-back {
-		position: fixed;
-		top: 0;
-		left: 0;
 		z-index: 1001;
-		width: 100%;
-		height: 100%;
-		background: var(--modalBg);
 	}
 
 	> .nav {
diff --git a/src/client/init.ts b/src/client/init.ts
index 7e11efe37ce607da65a214afd0dd47cf91d739f4..7a0339d3773323a400f80199af0145c16b2c4c05 100644
--- a/src/client/init.ts
+++ b/src/client/init.ts
@@ -106,36 +106,17 @@ document.body.innerHTML = '<div id="app"></div>';
 
 const store = createStore();
 
-const os = new MiOS(store);
-
-os.init(async () => {
-	window.addEventListener('storage', e => {
-		if (e.key === 'vuex') {
-			store.replaceState(JSON.parse(localStorage['vuex']));
-		} else if (e.key === 'i') {
-			location.reload();
-		}
-	}, false);
-
-	store.watch(state => state.device.darkMode, darkMode => {
-		import('./scripts/theme').then(({ builtinThemes }) => {
-			const themes = builtinThemes.concat(store.state.device.themes);
-			applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme)));
-		});
-	});
-
-	//#region Sync dark mode
-	if (store.state.device.syncDeviceDarkMode) {
-		store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
+window.addEventListener('storage', e => {
+	if (e.key === 'vuex') {
+		store.replaceState(JSON.parse(localStorage['vuex']));
+	} else if (e.key === 'i') {
+		location.reload();
 	}
+}, false);
 
-	window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
-		if (store.state.device.syncDeviceDarkMode) {
-			store.commit('device/set', { key: 'darkMode', value: mql.matches });
-		}
-	});
-	//#endregion
+const os = new MiOS(store);
 
+os.init(async () => {
 	//#region Fetch locale data
 	const i18n = new VueI18n();
 
@@ -148,13 +129,6 @@ os.init(async () => {
 	});
 	//#endregion
 
-	if ('Notification' in window && store.getters.isSignedIn) {
-		// 許可を得ていなかったらリクエスト
-		if (Notification.permission === 'default') {
-			Notification.requestPermission();
-		}
-	}
-
 	const app = new Vue({
 		store: store,
 		i18n,
@@ -228,6 +202,29 @@ os.init(async () => {
 	// マウント
 	app.$mount('#app');
 
+	store.watch(state => state.device.darkMode, darkMode => {
+		import('./scripts/theme').then(({ builtinThemes }) => {
+			const themes = builtinThemes.concat(store.state.device.themes);
+			applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme)));
+		});
+	});
+
+	//#region Sync dark mode
+	if (store.state.device.syncDeviceDarkMode) {
+		store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
+	}
+
+	window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
+		if (store.state.device.syncDeviceDarkMode) {
+			store.commit('device/set', { key: 'darkMode', value: mql.matches });
+		}
+	});
+	//#endregion
+
+	store.watch(state => state.device.useBlurEffectForModal, v => {
+		document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none');
+	}, { immediate: true });
+
 	os.stream.on('emojiAdded', data => {
 		// TODO
 		//store.commit('instance/set', );
@@ -263,6 +260,13 @@ os.init(async () => {
 	}
 
 	if (store.getters.isSignedIn) {
+		if ('Notification' in window) {
+			// 許可を得ていなかったらリクエスト
+			if (Notification.permission === 'default') {
+				Notification.requestPermission();
+			}
+		}
+
 		const main = os.stream.useSharedConnection('main');
 
 		// 自分の情報が更新されたとき
diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue
index 56c4a5699e198a2a38d2e672a082944431860007..3b56aa77bcbf5a7a5505b0dda1cedcb99195b85a 100644
--- a/src/client/pages/preferences/index.vue
+++ b/src/client/pages/preferences/index.vue
@@ -78,6 +78,7 @@
 			<mk-switch v-model="imageNewTab">{{ $t('openImageInNewTab') }}</mk-switch>
 			<mk-switch v-model="disableAnimatedMfm">{{ $t('disableAnimatedMfm') }}</mk-switch>
 			<mk-switch v-model="reduceAnimation">{{ $t('reduceUiAnimation') }}</mk-switch>
+			<mk-switch v-model="useBlurEffectForModal">{{ $t('useBlurEffectForModal') }}</mk-switch>
 			<mk-switch v-model="useOsNativeEmojis">
 				{{ $t('useOsNativeEmojis') }}
 				<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
@@ -178,6 +179,11 @@ export default Vue.extend({
 			set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); }
 		},
 
+		useBlurEffectForModal: {
+			get() { return this.$store.state.device.useBlurEffectForModal; },
+			set(value) { this.$store.commit('device/set', { key: 'useBlurEffectForModal', value: value }); }
+		},
+
 		disableAnimatedMfm: {
 			get() { return !this.$store.state.device.animatedMfm; },
 			set(value) { this.$store.commit('device/set', { key: 'animatedMfm', value: !value }); }
diff --git a/src/client/store.ts b/src/client/store.ts
index 31febc782b8148728a70cd675d3da2c18836f11d..a930e66ed7bb9647d618fb85f4abc7aa5b58d472 100644
--- a/src/client/store.ts
+++ b/src/client/store.ts
@@ -68,6 +68,7 @@ export const defaultDeviceSettings = {
 	disablePagesScript: true,
 	enableInfiniteScroll: true,
 	fixedWidgetsPosition: false,
+	useBlurEffectForModal: true,
 	roomGraphicsQuality: 'medium',
 	roomUseOrthographicCamera: true,
 	deckColumnAlign: 'left',
diff --git a/src/client/style.scss b/src/client/style.scss
index cc650ab1238dba96ee62336886dc1200039e9737..1a3eb09f4b90e807d32904d88b7ef89c2b2775c1 100644
--- a/src/client/style.scss
+++ b/src/client/style.scss
@@ -197,6 +197,16 @@ hr {
 	}
 }
 
+._modalBg {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background: var(--modalBg);
+	backdrop-filter: var(--modalBgFilter);
+}
+
 ._button {
 	appearance: none;
 	padding: 0;
diff --git a/src/client/themes/black.json5 b/src/client/themes/black.json5
index 3504f159328092e556c85344531c8af56f1188ca..06374284763156fe88acada88d23ba9a1829f8e0 100644
--- a/src/client/themes/black.json5
+++ b/src/client/themes/black.json5
@@ -12,6 +12,7 @@
 		panelHeaderBg: '@panel',
 		panelHeaderDivider: '@divider',
 		panelBorder: '@divider',
+		modalBg: 'rgba(255, 255, 255, 0.1)',
 		messageBg: '#1d1d1d',
 		deckColumnBorder: '@divider',
 	},