diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index a08308f0cef5caf3f37409badc9f4b1a9622d7f8..577337868f7f90d3f4036f3fae7b107714e91eab 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -5,22 +5,60 @@
 		<option value="block">{{ i18n.ts.blockedUsers }}</option>
 	</MkTab>
 	<div v-if="tab === 'mute'">
-		<MkPagination :pagination="mutingPagination" class="muting">
-			<template #empty><FormInfo>{{ i18n.ts.noUsers }}</FormInfo></template>
-			<template #default="{items}">
-				<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
-					<MkAcct :user="mute.mutee"/>
-				</FormLink>
+		<MkPagination :pagination="mutingPagination">
+			<template #empty>
+				<div class="_fullinfo">
+					<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+					<div>{{ i18n.ts.noUsers }}</div>
+				</div>
+			</template>
+
+			<template #default="{ items }">
+				<div class="_gaps_s">
+					<div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]">
+						<div :class="$style.userItemMain">
+							<MkA :class="$style.userItemMainBody" :to="`/user-info/${item.mutee.id}`">
+								<MkUserCardMini :user="item.mutee"/>
+							</MkA>
+							<button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button>
+							<button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button>
+						</div>
+						<div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub">
+							<div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div>
+							<div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div>
+							<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
+						</div>
+					</div>
+				</div>
 			</template>
 		</MkPagination>
 	</div>
 	<div v-if="tab === 'block'">
-		<MkPagination :pagination="blockingPagination" class="blocking">
-			<template #empty><FormInfo>{{ i18n.ts.noUsers }}</FormInfo></template>
-			<template #default="{items}">
-				<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
-					<MkAcct :user="block.blockee"/>
-				</FormLink>
+		<MkPagination :pagination="blockingPagination">
+			<template #empty>
+				<div class="_fullinfo">
+					<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+					<div>{{ i18n.ts.noUsers }}</div>
+				</div>
+			</template>
+
+			<template #default="{ items }">
+				<div class="_gaps_s">
+					<div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]">
+						<div :class="$style.userItemMain">
+							<MkA :class="$style.userItemMainBody" :to="`/user-info/${item.blockee.id}`">
+								<MkUserCardMini :user="item.blockee"/>
+							</MkA>
+							<button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button>
+							<button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button>
+						</div>
+						<div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub">
+							<div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div>
+							<div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div>
+							<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
+						</div>
+					</div>
+				</div>
 			</template>
 		</MkPagination>
 	</div>
@@ -36,6 +74,8 @@ import FormLink from '@/components/form/link.vue';
 import { userPage } from '@/filters/user';
 import { i18n } from '@/i18n';
 import { definePageMetadata } from '@/scripts/page-metadata';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+import * as os from '@/os';
 
 let tab = $ref('mute');
 
@@ -49,6 +89,47 @@ const blockingPagination = {
 	limit: 10,
 };
 
+let expandedMuteItems = $ref([]);
+let expandedBlockItems = $ref([]);
+
+async function unmute(user, ev) {
+	os.popupMenu([{
+		text: i18n.ts.unmute,
+		icon: 'ti ti-x',
+		action: async () => {
+			await os.apiWithDialog('mute/delete', { userId: user.id });
+			//role.users = role.users.filter(u => u.id !== user.id);
+		},
+	}], ev.currentTarget ?? ev.target);
+}
+
+async function unblock(user, ev) {
+	os.popupMenu([{
+		text: i18n.ts.unblock,
+		icon: 'ti ti-x',
+		action: async () => {
+			await os.apiWithDialog('blocking/delete', { userId: user.id });
+			//role.users = role.users.filter(u => u.id !== user.id);
+		},
+	}], ev.currentTarget ?? ev.target);
+}
+
+async function toggleMuteItem(item) {
+	if (expandedMuteItems.includes(item.id)) {
+		expandedMuteItems = expandedMuteItems.filter(x => x !== item.id);
+	} else {
+		expandedMuteItems.push(item.id);
+	}
+}
+
+async function toggleBlockItem(item) {
+	if (expandedBlockItems.includes(item.id)) {
+		expandedBlockItems = expandedBlockItems.filter(x => x !== item.id);
+	} else {
+		expandedBlockItems.push(item.id);
+	}
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
@@ -58,3 +139,43 @@ definePageMetadata({
 	icon: 'ti ti-ban',
 });
 </script>
+
+<style lang="scss" module>
+.userItemMain {
+	display: flex;
+}
+
+.userItemSub {
+	padding: 6px 12px;
+	font-size: 85%;
+	color: var(--fgTransparentWeak);
+}
+
+.userItemMainBody {
+	flex: 1;
+	min-width: 0;
+	margin-right: 8px;
+
+	&:hover {
+		text-decoration: none;
+	}
+}
+
+.userToggle,
+.remove {
+	width: 32px;
+	height: 32px;
+	align-self: center;
+}
+
+.chevron {
+	display: block;
+	transition: transform 0.1s ease-out;
+}
+
+.userItem.userItemOpend {
+	.chevron {
+		transform: rotateX(180deg);
+	}
+}
+</style>