diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 8dc49cf269998ad50517c4567ddb7c6b1d778b4a..ab5dff8db54adf0830b1cc72d7f9084ca49c400a 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -8,7 +8,7 @@
 	</template>
 
 	<template #default="{ items: notifications }">
-		<MkDateSeparatedList v-slot="{ item: notification }" class="elsfgstc" :items="notifications" :no-gap="true">
+		<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :no-gap="true">
 			<XNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/>
 			<XNotification v-else :key="notification.id" :notification="notification" :with-time="true" :full="true" class="_panel notification"/>
 		</MkDateSeparatedList>
@@ -97,8 +97,8 @@ onUnmounted(() => {
 });
 </script>
 
-<style lang="scss" scoped>
-.elsfgstc {
+<style lang="scss" module>
+.list {
 	background: var(--panel);
 }
 </style>
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index 681b3dcf25cae7821fa9f6c96932cb16c124bf28..25b9da2d0ba19e858c3690bf369e7fd24dcd87b1 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -17,7 +17,7 @@
 		</template>
 	</template>
 
-	<div class="yrolvcoq" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;">
+	<div :class="$style.root" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;">
 		<RouterView :router="router"/>
 	</div>
 </MkWindow>
@@ -133,8 +133,8 @@ defineExpose({
 });
 </script>
 
-<style lang="scss" scoped>
-.yrolvcoq {
+<style lang="scss" module>
+.root {
 	min-height: 100%;
 	background: var(--bg);
 
diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue
index ff2d18ed804c924d4fa94a63504959b51378ce7c..d037ecced899cda628ebf698776dd7ce7a2fa8b9 100644
--- a/packages/frontend/src/components/MkReactionEffect.vue
+++ b/packages/frontend/src/components/MkReactionEffect.vue
@@ -1,6 +1,6 @@
 <template>
 <div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }">
-	<span class="text" :class="{ up }">
+	<span :class="[$style.text, { [$style.up]: up }]">
 		<MkReactionIcon class="icon" :reaction="reaction"/>
 	</span>
 </div>
@@ -43,30 +43,28 @@ onMounted(() => {
 	position: fixed;
 	width: 128px;
 	height: 128px;
+}
 
-	&:global {
-		> .text {
-			display: block;
-			height: 1em;
-			text-align: center;
-			position: absolute;
-			top: 0;
-			left: 0;
-			right: 0;
-			bottom: 0;
-			margin: auto;
-			color: var(--accent);
-			font-size: 18px;
-			font-weight: bold;
-			transform: translateY(-30px);
-			transition: transform 1s cubic-bezier(0,.5,0,1), opacity 1s cubic-bezier(.5,0,1,.5);
-			will-change: opacity, transform;
+.text {
+	display: block;
+	height: 1em;
+	text-align: center;
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	color: var(--accent);
+	font-size: 18px;
+	font-weight: bold;
+	transform: translateY(-30px);
+	transition: transform 1s cubic-bezier(0,.5,0,1), opacity 1s cubic-bezier(.5,0,1,.5);
+	will-change: opacity, transform;
 
-			&.up {
-				opacity: 0;
-				transform: translateY(-50px) rotateZ(v-bind(angle));
-			}
-		}
+	&.up {
+		opacity: 0;
+		transform: translateY(-50px) rotateZ(v-bind(angle));
 	}
 }
 </style>
diff --git a/packages/frontend/src/components/MkReactionTooltip.vue b/packages/frontend/src/components/MkReactionTooltip.vue
index 3b3c2c1a1d229b41e634987d0037f875398c9717..4d67dc3da24a018bbe445a8846ec19479da34725 100644
--- a/packages/frontend/src/components/MkReactionTooltip.vue
+++ b/packages/frontend/src/components/MkReactionTooltip.vue
@@ -1,8 +1,8 @@
 <template>
 <MkTooltip ref="tooltip" :showing="showing" :target-element="targetElement" :max-width="340" @closed="emit('closed')">
-	<div class="beeadbfb">
-		<MkReactionIcon :reaction="reaction" class="icon" :no-style="true"/>
-		<div class="name">{{ reaction.replace('@.', '') }}</div>
+	<div :class="$style.root">
+		<MkReactionIcon :reaction="reaction" :class="$style.icon" :no-style="true"/>
+		<div :class="$style.name">{{ reaction.replace('@.', '') }}</div>
 	</div>
 </MkTooltip>
 </template>
@@ -23,20 +23,20 @@ const emit = defineEmits<{
 }>();
 </script>
 
-<style lang="scss" scoped>
-.beeadbfb {
+<style lang="scss" module>
+.root {
 	text-align: center;
+}
 
-	> .icon {
-		display: block;
-		width: 60px;
-		font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
-		margin: 0 auto;
-		object-fit: contain;
-	}
+.icon {
+	display: block;
+	width: 60px;
+	font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
+	margin: 0 auto;
+	object-fit: contain;
+}
 
-	> .name {
-		font-size: 0.9em;
-	}
+.name {
+	font-size: 0.9em;
 }
 </style>
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index 6ec227b0ed9dae36f2a4e0ac6c6ea9bfaf7a15e3..b4210be91175f9c4b1631e0503ce979aebb2ddd6 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -1,16 +1,16 @@
 <template>
 <MkTooltip ref="tooltip" :showing="showing" :target-element="targetElement" :max-width="340" @closed="emit('closed')">
-	<div class="bqxuuuey">
-		<div class="reaction">
-			<MkReactionIcon :reaction="reaction" class="icon" :no-style="true"/>
-			<div class="name">{{ getReactionName(reaction) }}</div>
+	<div :class="$style.root">
+		<div :class="$style.reaction">
+			<MkReactionIcon :reaction="reaction" :class="$style.reactionIcon" :no-style="true"/>
+			<div :class="$style.reactionName">{{ getReactionName(reaction) }}</div>
 		</div>
-		<div class="users">
-			<div v-for="u in users" :key="u.id" class="user">
-				<MkAvatar class="avatar" :user="u"/>
-				<MkUserName class="name" :user="u" :nowrap="true"/>
+		<div :class="$style.users">
+			<div v-for="u in users" :key="u.id" :class="$style.user">
+				<MkAvatar :class="$style.avatar" :user="u"/>
+				<MkUserName :user="u" :nowrap="true"/>
 			</div>
-			<div v-if="users.length > 10" class="omitted">+{{ count - 10 }}</div>
+			<div v-if="users.length > 10">+{{ count - 10 }}</div>
 		</div>
 	</div>
 </MkTooltip>
@@ -43,53 +43,53 @@ function getReactionName(reaction: string): string {
 }
 </script>
 
-<style lang="scss" scoped>
-.bqxuuuey {
+<style lang="scss" module>
+.root {
 	display: flex;
+}
 
-	> .reaction {
-		max-width: 100px;
-		text-align: center;
-
-		> .icon {
-			display: block;
-			width: 60px;
-			font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
-			object-fit: contain;
-			margin: 0 auto;
-		}
+.reaction {
+	max-width: 100px;
+	text-align: center;
+}
 
-		> .name {
-			font-size: 1em;
-		}
-	}
+.reactionIcon {
+	display: block;
+	width: 60px;
+	font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
+	object-fit: contain;
+	margin: 0 auto;
+}
 
-	> .users {
-		flex: 1;
-		min-width: 0;
-		font-size: 0.95em;
-		border-left: solid 0.5px var(--divider);
-		padding-left: 10px;
-		margin-left: 10px;
-		margin-right: 14px;
-		text-align: left;
+.reactionName {
+	font-size: 1em;
+}
 
-		> .user {
-			line-height: 24px;
-			white-space: nowrap;
-			overflow: hidden;
-			text-overflow: ellipsis;
+.users {
+	flex: 1;
+	min-width: 0;
+	font-size: 0.95em;
+	border-left: solid 0.5px var(--divider);
+	padding-left: 10px;
+	margin-left: 10px;
+	margin-right: 14px;
+	text-align: left;
+}
 
-			&:not(:last-child) {
-				margin-bottom: 3px;
-			}
+.user {
+	line-height: 24px;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
 
-			> .avatar {
-				width: 24px;
-				height: 24px;
-				margin-right: 3px;
-			}
-		}
+	&:not(:last-child) {
+		margin-bottom: 3px;
 	}
 }
+
+.avatar {
+	width: 24px;
+	height: 24px;
+	margin-right: 3px;
+}
 </style>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index cc6e87e1c4336037d751b6452978b874ee2f4d88..e90dd7ea697bafa8a3c88248168649e68846ed2e 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -2,12 +2,12 @@
 <button
 	ref="buttonEl"
 	v-ripple="canToggle"
-	class="hkzvhatu _button"
-	:class="{ reacted: note.myReaction == reaction, canToggle }"
+	class="_button"
+	:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle }]"
 	@click="toggleReaction()"
 >
-	<MkReactionIcon class="icon" :reaction="reaction"/>
-	<span class="count">{{ count }}</span>
+	<MkReactionIcon :class="$style.icon" :reaction="reaction"/>
+	<span :class="$style.count">{{ count }}</span>
 </button>
 </template>
 
@@ -92,8 +92,8 @@ useTooltip(buttonEl, async (showing) => {
 }, 100);
 </script>
 
-<style lang="scss" scoped>
-.hkzvhatu {
+<style lang="scss" module>
+.root {
 	display: inline-block;
 	height: 32px;
 	margin: 2px;
@@ -127,11 +127,11 @@ useTooltip(buttonEl, async (showing) => {
 			filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5));
 		}
 	}
+}
 
-	> .count {
-		font-size: 0.9em;
-		line-height: 32px;
-		margin: 0 0 0 4px;
-	}
+.count {
+	font-size: 0.9em;
+	line-height: 32px;
+	margin: 0 0 0 4px;
 }
 </style>
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index 724489bf866a9ab9bace1aa20e2326e92d720361..5981471c6887ff14e4cc1cc09edf45a4662d4fa4 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -1,5 +1,12 @@
 <template>
-<TransitionGroup :name="$store.state.animation ? 'x' : ''" tag="div" class="tdflqwzn" :class="{ isMe }">
+<TransitionGroup
+	:enter-active-class="$store.state.animation ? $style.transition_x_enterActive : ''"
+	:leave-active-class="$store.state.animation ? $style.transition_x_leaveActive : ''"
+	:enter-from-class="$store.state.animation ? $style.transition_x_enterFrom : ''"
+	:leave-to-class="$store.state.animation ? $style.transition_x_leaveTo : ''"
+	:move-class="$store.state.animation ? $style.transition_x_move : ''"
+	tag="div" :class="$style.root"
+>
 	<XReaction v-for="(count, reaction) in note.reactions" :key="reaction" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note"/>
 </TransitionGroup>
 </template>
@@ -19,29 +26,26 @@ const initialReactions = new Set(Object.keys(props.note.reactions));
 const isMe = computed(() => $i && $i.id === props.note.userId);
 </script>
 
-<style lang="scss" scoped>
-.x-move, .x-enter-active, .x-leave-active {
+<style lang="scss" module>
+.transition_x_move,
+.transition_x_enterActive,
+.transition_x_leaveActive {
 	transition: opacity 0.2s cubic-bezier(0,.5,.5,1), transform 0.2s cubic-bezier(0,.5,.5,1) !important;
 }
-.x-enter-from, .x-leave-to {
+.transition_x_enterFrom,
+.transition_x_leaveTo {
 	opacity: 0;
 	transform: scale(0.7);
 }
-.x-leave-active {
-  position: absolute;
+.transition_x_leaveActive {
+	position: absolute;
 }
 
-.tdflqwzn {
+.root {
 	margin: 4px -2px 0 -2px;
 
 	&:empty {
 		display: none;
 	}
-
-	&.isMe {
-		> span {
-			cursor: default !important;
-		}
-	}
 }
 </style>
diff --git a/packages/frontend/src/components/MkRemoteCaution.vue b/packages/frontend/src/components/MkRemoteCaution.vue
index 054c58da808d9ff01007daafa6a4a79501416a4b..f6081816ae394351d4138b3e0d4cffb367c39dc3 100644
--- a/packages/frontend/src/components/MkRemoteCaution.vue
+++ b/packages/frontend/src/components/MkRemoteCaution.vue
@@ -1,5 +1,5 @@
 <template>
-<div class="jmgmzlwq"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div>
+<div :class="$style.root"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a :class="$style.link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div>
 </template>
 
 <script lang="ts" setup>
@@ -10,18 +10,18 @@ defineProps<{
 }>();
 </script>
 
-<style lang="scss" scoped>
-.jmgmzlwq {
+<style lang="scss" module>
+.root {
 	font-size: 0.8em;
 	padding: 16px;
 	background: var(--infoWarnBg);
 	color: var(--infoWarnFg);
 	border-radius: var(--radius);
 	overflow: clip;
+}
 
-	> .link {
-		margin-left: 4px;
-		color: var(--accent);
-	}
+.link {
+	margin-left: 4px;
+	color: var(--accent);
 }
 </style>
diff --git a/packages/frontend/src/components/MkSparkle.vue b/packages/frontend/src/components/MkSparkle.vue
index 0f268a9d1aa515cdd11712b6b734b52af57c639a..ba1493aa716f2dbbe2c8f1e70ced261d6dccd342 100644
--- a/packages/frontend/src/components/MkSparkle.vue
+++ b/packages/frontend/src/components/MkSparkle.vue
@@ -1,6 +1,6 @@
 <template>
-<span class="mk-sparkle">
-	<span ref="el">
+<span :class="$style.root">
+	<span ref="el" style="display: inline-block;">
 		<slot></slot>
 	</span>
 	<!-- なぜか path に対する key が機能しないため
@@ -32,7 +32,7 @@
 		</path>
 	</svg>
 	-->
-	<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
+	<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -32px; left: -32px;">
 		<path
 			style="transform-origin: center; transform-box: fill-box;"
 			:transform="`translate(${particle.x} ${particle.y})`"
@@ -111,20 +111,10 @@ onUnmounted(() => {
 });
 </script>
 
-<style lang="scss" scoped>
-.mk-sparkle {
+<style lang="scss" module>
+.root {
 	position: relative;
 	display: inline-block;
-
-	> span {
-		display: inline-block;
-	}
-
-	> svg {
-		position: absolute;
-		top: -32px;
-		left: -32px;
-		pointer-events: none;
-	}
+	pointer-events: none;
 }
 </style>
diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue
index a4f6f80383e36f35a5eef24b2df5a8cd0385ed73..251ab5d79ae2533eee3ec4f09fc7d652d6bb9279 100644
--- a/packages/frontend/src/components/MkUserOnlineIndicator.vue
+++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue
@@ -1,5 +1,5 @@
 <template>
-<div v-tooltip="text" class="fzgwjkgc" :class="user.onlineStatus"></div>
+<div v-tooltip="text" :class="[$style.root, $style['status_' + user.onlineStatus]]"></div>
 </template>
 
 <script lang="ts" setup>
@@ -21,24 +21,24 @@ const text = $computed(() => {
 });
 </script>
 
-<style lang="scss" scoped>
-.fzgwjkgc {
+<style lang="scss" module>
+.root {
 	box-shadow: 0 0 0 3px var(--panel);
 	border-radius: 120%; // Blinkのバグか知らんけど、100%ぴったりにすると何故か若干楕円でレンダリングされる
 
-	&.online {
+	&.status_online {
 		background: #58d4c9;
 	}
 
-	&.active {
+	&.status_active {
 		background: #e4bc48;
 	}
 
-	&.offline {
+	&.status_offline {
 		background: #ea5353;
 	}
 
-	&.unknown {
+	&.status_unknown {
 		background: #888;
 	}
 }