diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d1434cde42ccf3ecdbe8da985765d93fc7b91f1..e5ff09edeca2a21d56e1009eb4f0aee9d9d5f2fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
+- Enhance: アイコンデコレーションを複数設定できるように
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 846a6d503dbabe59a8f3b75d21a4a692ce06da56..d32023f5ac75a5919a891ce99a1f387c7abc83e9 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -264,6 +264,7 @@ export interface Locale {
     "removeAreYouSure": string;
     "deleteAreYouSure": string;
     "resetAreYouSure": string;
+    "areYouSure": string;
     "saved": string;
     "messaging": string;
     "upload": string;
@@ -1160,6 +1161,7 @@ export interface Locale {
     "avatarDecorations": string;
     "attach": string;
     "detach": string;
+    "detachAll": string;
     "angle": string;
     "flip": string;
     "showAvatarDecorations": string;
@@ -1173,6 +1175,7 @@ export interface Locale {
     "doReaction": string;
     "code": string;
     "reloadRequiredToApplySettings": string;
+    "remainingN": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
@@ -1701,6 +1704,7 @@ export interface Locale {
             "canHideAds": string;
             "canSearchNotes": string;
             "canUseTranslator": string;
+            "avatarDecorationLimit": string;
         };
         "_condition": {
             "isLocal": string;
@@ -2181,6 +2185,7 @@ export interface Locale {
         "changeAvatar": string;
         "changeBanner": string;
         "verifiedLinkDescription": string;
+        "avatarDecorationMax": string;
     };
     "_exportOrImport": {
         "allNotes": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0d84440bc868278c623b9f462a83e4f0ff1b5127..2ac57fd3111a9177d807061d5e00320298d8dffb 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -261,6 +261,7 @@ removed: "削除しました"
 removeAreYouSure: "「{x}」を削除しますか?"
 deleteAreYouSure: "「{x}」を削除しますか?"
 resetAreYouSure: "リセットしますか?"
+areYouSure: "よろしいですか?"
 saved: "保存しました"
 messaging: "チャット"
 upload: "アップロード"
@@ -1157,6 +1158,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
 avatarDecorations: "アイコンデコレーション"
 attach: "付ける"
 detach: "外す"
+detachAll: "全て外す"
 angle: "角度"
 flip: "反転"
 showAvatarDecorations: "アイコンのデコレーションを表示"
@@ -1170,6 +1172,7 @@ cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述
 doReaction: "リアクションする"
 code: "コード"
 reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
+remainingN: "残り: {n}"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
@@ -1610,6 +1613,7 @@ _role:
     canHideAds: "広告の非表示"
     canSearchNotes: "ノート検索の利用"
     canUseTranslator: "翻訳機能の利用"
+    avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
   _condition:
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
@@ -2084,6 +2088,7 @@ _profile:
   changeAvatar: "アイコン画像を変更"
   changeBanner: "バナー画像を変更"
   verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。"
+  avatarDecorationMax: "最大{max}つまでデコレーションを付けられます。"
 
 _exportOrImport:
   allNotes: "全てのノート"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 29e48aa8ca0d79fb408a3aae3ac8e156ae6a4b41..4de719d6a06b9a71dbb9b0fdac3dfb9d00dee09a 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -47,6 +47,7 @@ export type RolePolicies = {
 	userListLimit: number;
 	userEachUserListsLimit: number;
 	rateLimitFactor: number;
+	avatarDecorationLimit: number;
 };
 
 export const DEFAULT_POLICIES: RolePolicies = {
@@ -73,6 +74,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	userListLimit: 10,
 	userEachUserListsLimit: 50,
 	rateLimitFactor: 1,
+	avatarDecorationLimit: 1,
 };
 
 @Injectable()
@@ -326,6 +328,7 @@ export class RoleService implements OnApplicationShutdown {
 			userListLimit: calc('userListLimit', vs => Math.max(...vs)),
 			userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)),
 			rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)),
+			avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)),
 		};
 	}
 
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index dd2f32b14d7768d1c26bdbaf87ad2f06601c1b13..b0c6804bb8f5021be0a3cad4c73b9581b2477728 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -145,6 +145,7 @@ export const packedRoleSchema = {
 						userEachUserListsLimit: rolePolicyValue,
 						canManageAvatarDecorations: rolePolicyValue,
 						canUseTranslator: rolePolicyValue,
+						avatarDecorationLimit: rolePolicyValue,
 					},
 				},
 				usersCount: {
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index c6b2707b80ed08c23a1147e1618f636d51a8107c..c6b96b85f0728f5b2bee5dbcb727455fbac58097 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -672,6 +672,10 @@ export const packedMeDetailedOnlySchema = {
 					type: 'number',
 					nullable: false, optional: false,
 				},
+				avatarDecorationLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
 			},
 		},
 		//#region secrets
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index b045c011891e50a31bcfbcd06d84db7a2078bfce..399e6b88cb35d3e5077c31d79ee6d11641dea8e1 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -125,7 +125,7 @@ export const meta = {
 
 const muteWords = { type: 'array', items: { oneOf: [
 	{ type: 'array', items: { type: 'string' } },
-	{ type: 'string' }
+	{ type: 'string' },
 ] } } as const;
 
 export const paramDef = {
@@ -137,7 +137,7 @@ export const paramDef = {
 		birthday: { ...birthdaySchema, nullable: true },
 		lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true },
 		avatarId: { type: 'string', format: 'misskey:id', nullable: true },
-		avatarDecorations: { type: 'array', maxItems: 1, items: {
+		avatarDecorations: { type: 'array', maxItems: 16, items: {
 			type: 'object',
 			properties: {
 				id: { type: 'string', format: 'misskey:id' },
@@ -251,7 +251,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			function validateMuteWordRegex(mutedWords: (string[] | string)[]) {
 				for (const mutedWord of mutedWords) {
-					if (typeof mutedWord !== "string") continue;
+					if (typeof mutedWord !== 'string') continue;
 
 					const regexp = mutedWord.match(/^\/(.+)\/(.*)$/);
 					if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
@@ -329,12 +329,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (ps.avatarDecorations) {
 				const decorations = await this.avatarDecorationService.getAll(true);
-				const myRoles = await this.roleService.getUserRoles(user.id);
+				const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
 				const allRoles = await this.roleService.getRoles();
 				const decorationIds = decorations
 					.filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
 					.map(d => d.id);
 
+				if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
+
 				updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({
 					id: d.id,
 					angle: d.angle ?? 0,
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index 0e4e4b50ff90a602017f8ad0dd0a2be7b2a29819..a6af298024d2bdac518d8b1a65b68fdea89a74ec 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -284,7 +284,7 @@ export async function openAccountMenu(opts: {
 			text: i18n.ts.profile,
 			to: `/@${ $i.username }`,
 			avatar: $i,
-		}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
+		}, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
 			type: 'parent' as const,
 			icon: 'ti ti-plus',
 			text: i18n.ts.addAccount,
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 53226646649585ad3dec1b6fdef91dca8b7fe7c8..b0c14d1f0bc131ff55f43c0c6b9db32c100233cd 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -39,6 +39,7 @@ import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { claimAchievement } from '@/scripts/achievements.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { MenuItem } from '@/types/menu.js';
 
 const props = withDefaults(defineProps<{
 	folder: Misskey.entities.DriveFolder;
@@ -250,7 +251,7 @@ function setAsUploadFolder() {
 }
 
 function onContextmenu(ev: MouseEvent) {
-	let menu;
+	let menu: MenuItem[];
 	menu = [{
 		text: i18n.ts.openInWindow,
 		icon: 'ti ti-app-window',
@@ -260,18 +261,18 @@ function onContextmenu(ev: MouseEvent) {
 			}, {
 			}, 'closed');
 		},
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.rename,
 		icon: 'ti ti-forms',
 		action: rename,
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.delete,
 		icon: 'ti ti-trash',
 		danger: true,
 		action: deleteFolder,
 	}];
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyFolderId,
 			action: () => {
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 1150a29e03d702bae73ff522f272cc3c63382266..7c8ffcccf9af9461652431cee90f5d2719eb9850 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
 import contains from '@/scripts/contains.js';
 import * as os from '@/os.js';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 809dae421ab6a4c2c3944efb2114d9af6a829fe4..5552e96ee09de48ebd62b25dac862f0fefdb67a0 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -57,7 +57,7 @@ function onContextmenu(ev) {
 		action: () => {
 			router.push(props.to, 'forcePage');
 		},
-	}, null, {
+	}, { type: 'divider' }, {
 		icon: 'ti ti-external-link',
 		text: i18n.ts.openInNewTab,
 		action: () => {
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index c7e50e275a3cf10a36fe59a2e0e8bcf92f7455a7..6aa9a42037c2e6efcbf78010bad8c68d206d8fdd 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -23,16 +23,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 		</div>
 	</div>
-	<img
-		v-if="showDecoration && (decoration || user.avatarDecorations.length > 0)"
-		:class="[$style.decoration]"
-		:src="decoration?.url ?? user.avatarDecorations[0].url"
-		:style="{
-			rotate: getDecorationAngle(),
-			scale: getDecorationScale(),
-		}"
-		alt=""
-	>
+	<template v-if="showDecoration">
+		<img
+			v-for="decoration in decorations ?? user.avatarDecorations"
+			:class="[$style.decoration]"
+			:src="decoration.url"
+			:style="{
+				rotate: getDecorationAngle(decoration),
+				scale: getDecorationScale(decoration),
+			}"
+			alt=""
+		>
+	</template>
 </component>
 </template>
 
@@ -57,19 +59,14 @@ const props = withDefaults(defineProps<{
 	link?: boolean;
 	preview?: boolean;
 	indicator?: boolean;
-	decoration?: {
-		url: string;
-		angle?: number;
-		flipH?: boolean;
-		flipV?: boolean;
-	};
+	decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][];
 	forceShowDecoration?: boolean;
 }>(), {
 	target: null,
 	link: false,
 	preview: false,
 	indicator: false,
-	decoration: undefined,
+	decorations: undefined,
 	forceShowDecoration: false,
 });
 
@@ -92,27 +89,13 @@ function onClick(ev: MouseEvent): void {
 	emit('click', ev);
 }
 
-function getDecorationAngle() {
-	let angle;
-	if (props.decoration) {
-		angle = props.decoration.angle ?? 0;
-	} else if (props.user.avatarDecorations.length > 0) {
-		angle = props.user.avatarDecorations[0].angle ?? 0;
-	} else {
-		angle = 0;
-	}
+function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+	const angle = decoration.angle ?? 0;
 	return angle === 0 ? undefined : `${angle * 360}deg`;
 }
 
-function getDecorationScale() {
-	let scaleX;
-	if (props.decoration) {
-		scaleX = props.decoration.flipH ? -1 : 1;
-	} else if (props.user.avatarDecorations.length > 0) {
-		scaleX = props.user.avatarDecorations[0].flipH ? -1 : 1;
-	} else {
-		scaleX = 1;
-	}
+function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+	const scaleX = decoration.flipH ? -1 : 1;
 	return scaleX === 1 ? undefined : `${scaleX} 1`;
 }
 
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 397f80482236012b883ef9e373cad09fc32a7587..f016b7aa02854394411a51be46df62b54a3079c6 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -81,6 +81,7 @@ export const ROLE_POLICIES = [
 	'userListLimit',
 	'userEachUserListsLimit',
 	'rateLimitFactor',
+	'avatarDecorationLimit',
 ] as const;
 
 // なんか動かない
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index a8e0e8bbd19f5db5ada47cec997824c5d65eef37..5ded8d6931ce0a3fe7c272307a3ea0f01af9bef0 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -531,6 +531,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkRange>
 				</div>
 			</MkFolder>
+
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+				<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+				<template #suffix>
+					<span v-if="role.policies.avatarDecorationLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.avatarDecorationLimit.value }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.avatarDecorationLimit)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.avatarDecorationLimit.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkInput v-model="role.policies.avatarDecorationLimit.value" type="number" :min="0">
+						<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+					</MkInput>
+					<MkRange v-model="role.policies.avatarDecorationLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
 		</div>
 	</FormSlot>
 </div>
@@ -549,7 +569,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkRange from '@/components/MkRange.vue';
 import FormSlot from '@/components/form/slot.vue';
 import { i18n } from '@/i18n.js';
-import { ROLE_POLICIES } from '@/const';
+import { ROLE_POLICIES } from '@/const.js';
 import { instance } from '@/instance.js';
 import { deepClone } from '@/scripts/clone.js';
 
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index db4595b150fe23161088e9705d06a74b4b5cfeb4..1bb91a0a5bd1f139f5cbde61f7215e93f8502451 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -192,6 +192,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+							<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+							<template #suffix>{{ policies.avatarDecorationLimit }}</template>
+							<MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0">
+							</MkInput>
+						</MkFolder>
+
 						<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton>
 					</div>
 				</MkFolder>
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
index 4d571bc9ba8f14bc2793fc8565cf16f1818e01cc..c27a21217b562b8b7b2f9488d883581e6129acbd 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSpacer :marginMin="20" :marginMax="28">
 			<div style="text-align: center;">
 				<div :class="$style.name">{{ decoration.name }}</div>
-				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decoration="{ url: decoration.url, angle, flipH }" forceShowDecoration/>
+				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="[...$i.avatarDecorations, { url: decoration.url, angle, flipH }]" forceShowDecoration/>
 			</div>
 			<div class="_gaps_s">
 				<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
@@ -54,6 +54,7 @@ const props = defineProps<{
 	decoration: {
 		id: string;
 		url: string;
+		name: string;
 	}
 }>();
 
@@ -77,18 +78,18 @@ async function attach() {
 		flipH: flipH.value,
 	};
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: [decoration],
+		avatarDecorations: [...$i.avatarDecorations, decoration],
 	});
-	$i.avatarDecorations = [decoration];
+	$i.avatarDecorations = [...$i.avatarDecorations, decoration];
 
 	dialog.value.close();
 }
 
 async function detach() {
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: [],
+		avatarDecorations: $i.avatarDecorations.filter(x => x.id !== props.decoration.id),
 	});
-	$i.avatarDecorations = [];
+	$i.avatarDecorations = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
 
 	dialog.value.close();
 }
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index ba75b539e1c8f3d6e5b4f4581e2b45b1bd4fcd87..a5d3835b934dcc8eb15e2d1f89774bc9f17b63e8 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -87,16 +87,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #icon><i class="ti ti-sparkles"></i></template>
 		<template #label>{{ i18n.ts.avatarDecorations }}</template>
 
-		<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
-			<div
-				v-for="avatarDecoration in avatarDecorations"
-				:key="avatarDecoration.id"
-				:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
-				@click="openDecoration(avatarDecoration)"
-			>
-				<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
-				<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decoration="{ url: avatarDecoration.url }" forceShowDecoration/>
-				<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
+		<div class="_gaps">
+			<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
+
+			<MkButton v-if="$i.avatarDecorations.length > 0" danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
+
+			<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
+				<div
+					v-for="avatarDecoration in avatarDecorations"
+					:key="avatarDecoration.id"
+					:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
+					@click="openDecoration(avatarDecoration)"
+				>
+					<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
+					<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: avatarDecoration.url }]" forceShowDecoration/>
+					<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
+				</div>
 			</div>
 		</div>
 	</MkFolder>
@@ -273,6 +279,19 @@ function openDecoration(avatarDecoration) {
 	}, {}, 'closed');
 }
 
+function detachAllDecorations() {
+	os.confirm({
+		type: 'warning',
+		text: i18n.ts.areYouSure,
+	}).then(async ({ canceled }) => {
+		if (canceled) return;
+		await os.apiWithDialog('i/update', {
+			avatarDecorations: [],
+		});
+		$i.avatarDecorations = [];
+	});
+}
+
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);