diff --git a/libs/collapsible/src/lib/collapsible/collapsible.component.html b/libs/collapsible/src/lib/collapsible/collapsible.component.html
index b050a2de5..e547633c6 100644
--- a/libs/collapsible/src/lib/collapsible/collapsible.component.html
+++ b/libs/collapsible/src/lib/collapsible/collapsible.component.html
@@ -9,8 +9,10 @@
         tabindex="0"
     >
         <ng-container *ngIf="alwaysRender || active">
-            <ng-content></ng-content>
-            <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
+            <div [attr.inert]="!active ? true : null" class="ant-collapse-content-inner">
+                <ng-content></ng-content>
+                <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
+            </div>
         </ng-container>
     </nz-collapse-panel>
 </nz-collapse>
diff --git a/libs/collapsible/src/lib/collapsible/collapsible.component.less b/libs/collapsible/src/lib/collapsible/collapsible.component.less
index 84829b419..c32ec3d7f 100644
--- a/libs/collapsible/src/lib/collapsible/collapsible.component.less
+++ b/libs/collapsible/src/lib/collapsible/collapsible.component.less
@@ -111,4 +111,8 @@
         background: @gray-lighter;
         color: @ink;
     }
+
+    &-content-inner {
+        display: contents;
+    }
 }
diff --git a/libs/collapsible/src/lib/collapsible/collapsible.component.stories.ts b/libs/collapsible/src/lib/collapsible/collapsible.component.stories.ts
index b4de1437c..2b3003ea3 100644
--- a/libs/collapsible/src/lib/collapsible/collapsible.component.stories.ts
+++ b/libs/collapsible/src/lib/collapsible/collapsible.component.stories.ts
@@ -1,12 +1,12 @@
 import { Component } from '@angular/core';
 import { provideAnimations } from '@angular/platform-browser/animations';
 import { applicationConfig, Meta, moduleMetadata } from '@storybook/angular';
-import { CollapsibleComponent } from './collapsible.component';
 import { CollapsibleModule } from '../collapsible.module';
+import { CollapsibleComponent } from './collapsible.component';
 
 @Component({
     selector: 'spy-story',
-    template: ` Collapse Content `,
+    template: `<input /> Collapse Content `,
 })
 class StoryComponent {
     constructor() {
diff --git a/libs/dropdown/src/lib/dropdown/dropdown.component.html b/libs/dropdown/src/lib/dropdown/dropdown.component.html
index 30a9bb243..18295fafb 100644
--- a/libs/dropdown/src/lib/dropdown/dropdown.component.html
+++ b/libs/dropdown/src/lib/dropdown/dropdown.component.html
@@ -6,28 +6,55 @@
     [(nzVisible)]="visible"
     [nzTrigger]="trigger"
     (click)="$event.stopPropagation()"
+    (keydown)="onKeyDown($event)"
     (nzVisibleChange)="visibleChange.emit($event)"
+    tabindex="0"
+    [attr.aria-label]="ariaLabel"
 >
     <ng-content></ng-content>
 </span>
+
 <nz-dropdown-menu #menu="nzDropdownMenu">
-    <ul nz-menu>
-        <ng-container *ngTemplateOutlet="dropdownTpl; context: { $implicit: items }"></ng-container>
+    <ul nz-menu (keydown)="onMenuKeyDown($event)">
+        <ng-container *ngTemplateOutlet="dropdownTpl; context: { $implicit: items, index: 0 }"></ng-container>
 
-        <ng-template #dropdownTpl let-items>
-            <ng-container *ngFor="let item of items">
-                <li *ngIf="!item.subItems" (click)="actionTriggered.emit(item.action)" nz-menu-item>
-                    <spy-icon class="spy-dropdown-item__icon" *ngIf="item.icon" [name]="item.icon"></spy-icon>
-                    <span class="spy-dropdown-item__text">{{ item.title }}</span>
-                </li>
-                <li *ngIf="item.subItems" (click)="actionTriggered.emit(item.action)" nz-submenu>
-                    <ul>
-                        <ng-container
-                            *ngTemplateOutlet="dropdownTpl; context: { $implicit: item.subItems }"
-                        ></ng-container>
-                    </ul>
-                </li>
-            </ng-container>
+        <ng-template #dropdownTpl let-items let-index="index">
+            @for (item of items; track $index) {
+                @if (item.subItems) {
+                    <li
+                        [attr.aria-label]="item.title"
+                        [attr.tabindex]="item.disabled ? null : '0'"
+                        #dropdownItem
+                        (click)="onItemClick(item.action)"
+                        (keydown)="onItemKeyDown($event, index)"
+                        nz-submenu
+                        [nzIcon]="item.icon"
+                        [nzTitle]="item.title"
+                        [nzDisabled]="item.disabled"
+                        class="spy-dropdown-item__item spy-dropdown-item__item--submenu"
+                    >
+                        <ul #submenu (keydown)="onMenuKeyDown($event, index)">
+                            <ng-container
+                                *ngTemplateOutlet="dropdownTpl; context: { $implicit: item.subItems, index: index + 1 }"
+                            ></ng-container>
+                        </ul>
+                    </li>
+                } @else {
+                    <li
+                        [attr.aria-label]="item.title"
+                        [attr.tabindex]="item.disabled ? null : '0'"
+                        [nzDisabled]="item.disabled"
+                        #dropdownItem
+                        (click)="onItemClick(item.action)"
+                        (keydown)="onItemKeyDown($event)"
+                        nz-menu-item
+                        class="spy-dropdown-item__item"
+                    >
+                        <spy-icon class="spy-dropdown-item__icon" *ngIf="item.icon" [name]="item.icon"></spy-icon>
+                        <span class="spy-dropdown-item__text">{{ item.title }}</span>
+                    </li>
+                }
+            }
         </ng-template>
     </ul>
 </nz-dropdown-menu>
diff --git a/libs/dropdown/src/lib/dropdown/dropdown.component.less b/libs/dropdown/src/lib/dropdown/dropdown.component.less
index 38da4f6b7..d7b3368cc 100644
--- a/libs/dropdown/src/lib/dropdown/dropdown.component.less
+++ b/libs/dropdown/src/lib/dropdown/dropdown.component.less
@@ -9,6 +9,11 @@
     &-trigger {
         display: flex;
         height: 100%;
+
+        &:focus-visible {
+            outline: @outline;
+            outline-offset: @outline-offset;
+        }
     }
 
     &-menu {
@@ -18,26 +23,32 @@
         background-color: @spy-white;
         padding: @dropdown-menu-padding;
         overflow: hidden;
+    }
+
+    &-menu-submenu-title,
+    &-menu-item {
+        height: @dropdown-menu-item-height;
+        padding: @dropdown-menu-item-padding;
+        font: @font-default;
+        color: @ink;
+        border: @dropdown-menu-item-border;
+        display: flex;
+        align-items: center;
 
-        &-item {
-            height: @dropdown-menu-item-height;
-            padding: @dropdown-menu-item-padding;
-            font: @font-default;
-            color: @ink;
-            border: @dropdown-menu-item-border;
-            display: flex;
-            align-items: center;
-
-            &:hover {
-                background-color: @gray-lighter;
-                border-color: @gray-lighter;
-            }
-
-            &:focus {
-                border-radius: @dropdown-menu-item-border-focus-radius;
-                border-color: @green;
-            }
+        &:hover {
+            background-color: @gray-lighter;
+            border-color: @gray-lighter;
         }
+
+        &:focus {
+            border-radius: @dropdown-menu-item-border-focus-radius;
+            border-color: @green;
+        }
+    }
+
+    &-menu-submenu:focus .@{dropdown-prefix-cls}-menu-submenu-title {
+        border-radius: @dropdown-menu-item-border-focus-radius;
+        border-color: @green;
     }
 
     .@{menu-prefix-cls}-title-content {
diff --git a/libs/dropdown/src/lib/dropdown/dropdown.component.stories.ts b/libs/dropdown/src/lib/dropdown/dropdown.component.stories.ts
index 9f045d95f..dc2919594 100644
--- a/libs/dropdown/src/lib/dropdown/dropdown.component.stories.ts
+++ b/libs/dropdown/src/lib/dropdown/dropdown.component.stories.ts
@@ -38,7 +38,28 @@ export default {
     },
     args: {
         items: [
-            { action: 'action1', title: 'item1' },
+            {
+                action: 'action1',
+                title: 'item1',
+                subItems: [
+                    {
+                        action: 'action1',
+                        title: 'subItem1',
+                        subItems: [
+                            { action: 'action1', title: 'subItem1' },
+                            { action: 'action1', title: 'subItem2' },
+                            { action: 'action1', title: 'subItem3' },
+                            { action: 'action1', title: 'subItem4' },
+                        ],
+                    },
+                    { action: 'action1', title: 'subItem2' },
+                    { action: 'action1', title: 'subItem3' },
+                    { action: 'action1', title: 'subItem4' },
+                ],
+            },
+            { action: 'action2', title: 'item2' },
+            { action: 'action2', title: 'item2' },
+            { action: 'action2', title: 'item2' },
             { action: 'action2', title: 'item2' },
         ],
         placement: 'bottomRight',
@@ -56,9 +77,12 @@ export const primary = (args) => ({
         [items]="items"
         [placement]="placement"
         [visible]="visible"
+        [trigger]="trigger"
         [disabled]="disabled">
         {{ trigger }} me
       </spy-dropdown>
+
+      <input />
     </div>
   `,
 });
diff --git a/libs/dropdown/src/lib/dropdown/dropdown.component.ts b/libs/dropdown/src/lib/dropdown/dropdown.component.ts
index 6ecc8ff38..2c3d63b58 100644
--- a/libs/dropdown/src/lib/dropdown/dropdown.component.ts
+++ b/libs/dropdown/src/lib/dropdown/dropdown.component.ts
@@ -2,12 +2,17 @@ import {
     booleanAttribute,
     ChangeDetectionStrategy,
     Component,
+    ElementRef,
     EventEmitter,
-    HostBinding,
     Input,
     Output,
+    QueryList,
+    ViewChild,
+    ViewChildren,
     ViewEncapsulation,
 } from '@angular/core';
+import { NzDropDownDirective } from 'ng-zorro-antd/dropdown';
+import { NzMenuDirective, NzSubMenuComponent } from 'ng-zorro-antd/menu';
 
 export interface DropdownItem {
     action: string;
@@ -25,15 +30,111 @@ export type Trigger = 'click' | 'hover';
     styleUrls: ['./dropdown.component.less'],
     changeDetection: ChangeDetectionStrategy.OnPush,
     encapsulation: ViewEncapsulation.None,
+    host: {
+        '[class.spy-dropdown--open]': 'visible',
+    },
 })
 export class DropdownComponent {
     @Input() items: DropdownItem[] = [];
     @Input() placement: Placement = 'bottomRight';
     @Input() trigger: Trigger = 'hover';
-    @HostBinding('class.spy-dropdown--open')
-    @Input({ transform: booleanAttribute })
-    visible = false;
+    @Input() ariaLabel: string = null;
+    @Input({ transform: booleanAttribute }) visible = true;
     @Input({ transform: booleanAttribute }) disabled = false;
     @Output() visibleChange = new EventEmitter<boolean>();
     @Output() actionTriggered = new EventEmitter<string>();
+
+    @ViewChild(NzDropDownDirective, { read: ElementRef }) menuTrigger: ElementRef<HTMLElement>;
+    @ViewChild(NzMenuDirective, { read: ElementRef }) menu: ElementRef<HTMLElement>;
+
+    @ViewChildren(NzSubMenuComponent) submenuTriggers: QueryList<NzSubMenuComponent>;
+    @ViewChildren('submenu') submenuElements: QueryList<ElementRef<HTMLElement>>;
+
+    onKeyDown(event: KeyboardEvent): void {
+        if (event.key === 'Tab' && document.activeElement === this.menuTrigger.nativeElement) {
+            this.visible = false;
+
+            return;
+        }
+
+        const events = ['Enter', ' ', 'ArrowDown'];
+
+        if (!events.includes(event.key) || !this.items.length) {
+            return;
+        }
+
+        event.preventDefault();
+
+        if (event.key === 'ArrowDown') {
+            this.getMenuItems(this.menu.nativeElement)[0].focus();
+
+            return;
+        }
+
+        this.visible = !this.visible;
+    }
+
+    onMenuKeyDown(event: KeyboardEvent, submenuIndex?: number): void {
+        const events = ['ArrowUp', 'Tab', 'ArrowDown'];
+        const items = this.getMenuItems(event.currentTarget as HTMLElement);
+        const { length } = items;
+
+        if (!events.includes(event.key) || !length) {
+            return;
+        }
+
+        event.preventDefault();
+
+        if (event.key === 'Tab' && event.shiftKey) {
+            const focusElement =
+                submenuIndex === undefined
+                    ? this.menuTrigger.nativeElement
+                    : this.getMenuItems(
+                          submenuIndex === 0
+                              ? this.menu.nativeElement
+                              : this.submenuElements.get(submenuIndex - 1).nativeElement,
+                      )[0];
+
+            focusElement.focus();
+            this.visible = submenuIndex !== undefined;
+            this.submenuTriggers.get(submenuIndex)?.setOpenStateWithoutDebounce(false);
+
+            return;
+        }
+
+        const currentIndex = items.findIndex((item) => item === document.activeElement);
+        const index = event.key === 'ArrowUp' ? (currentIndex - 1 + length) % length : (currentIndex + 1) % length;
+
+        items[index].focus();
+    }
+
+    onItemKeyDown(event: KeyboardEvent, submenuIndex?: number): void {
+        if (event.key !== ' ' && event.key !== 'Enter') {
+            return;
+        }
+
+        const target = event.target as HTMLElement;
+
+        target.click();
+
+        if (submenuIndex !== undefined) {
+            this.submenuTriggers.get(submenuIndex).setOpenStateWithoutDebounce(true);
+
+            setTimeout(() => {
+                this.getMenuItems(this.submenuElements.get(submenuIndex).nativeElement)[0].focus();
+            });
+
+            return;
+        }
+
+        this.submenuTriggers.forEach((trigger) => trigger.setOpenStateWithoutDebounce(false));
+    }
+
+    onItemClick(action: string): void {
+        this.actionTriggered.emit(action);
+    }
+
+    private getMenuItems(menu: HTMLElement): HTMLElement[] {
+        return Array.from(menu.querySelectorAll('.spy-dropdown-item__item'));
+    }
 }
diff --git a/libs/styles/src/lib/themes/default/variables/common.less b/libs/styles/src/lib/themes/default/variables/common.less
index bde6a4b38..3d8efad81 100644
--- a/libs/styles/src/lib/themes/default/variables/common.less
+++ b/libs/styles/src/lib/themes/default/variables/common.less
@@ -17,6 +17,11 @@
 @box-shadow-2: var(--spy-box-shadow-2, 1px 3px 18px @ink-effect);
 @box-shadow-3: var(--spy-box-shadow-3, -2px 2px 20px @ink-effect);
 
+@outline-color: var(--spy-outline-color, @green);
+@outline-width: var(--spy-outline-width, 1px);
+@outline: var(--spy-outline, @outline-width solid @outline-color);
+@outline-offset: var(--spy-outline-offset, 2px);
+
 // Breakpoints
 
 /* stylelint-disable property-no-unknown */