diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts
index dd996ceb554faca8a93db44f758dbe057163e438..a3ae962af474a3cf0a5cd2aa6744996e65383611 100644
--- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts
+++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts
@@ -3,14 +3,16 @@ import { Component } from '@angular/core';
 @Component({
   selector: 'ods-tooltip',
   template: `<p
-    class="fixed z-50 mt-2 -translate-x-1/2 animate-fadeIn rounded bg-ozggray-900 px-3 py-2 text-sm text-whitetext before:absolute before:-top-2 before:left-[calc(50%-0.5rem)] before:size-0 before:border-b-8 before:border-l-8 before:border-r-8 before:border-b-ozggray-900 before:border-l-transparent before:border-r-transparent before:content-[''] dark:bg-white dark:before:border-b-white"
+    class="fixed z-50 mt-2 -translate-x-1/2 animate-fadeIn cursor-default rounded bg-ozggray-900 px-3 py-2 text-sm text-whitetext before:absolute before:-top-2 before:left-[calc(50%-0.5rem)] before:size-0 before:border-b-8 before:border-l-8 before:border-r-8 before:border-b-ozggray-900 before:border-l-transparent before:border-r-transparent before:content-[''] dark:bg-white dark:before:border-b-white"
     [style.left]="left + 'px'"
     [style.top]="top + 'px'"
     [attr.id]="id"
+    role="tooltip"
   >
     {{ text }}
   </p>`,
   styles: [':host {@apply contents}'],
+  standalone: true,
 })
 export class TooltipComponent {
   text: string = '';
diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts
index 7047e82146d33a4c82584f4c85f531fcf2a85fbd..07895edc7c9aa5622948043a663ea3c940e0375b 100644
--- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts
+++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts
@@ -1,34 +1,221 @@
-import { ElementRef, ViewContainerRef } from '@angular/core';
+import { InteractivityChecker } from '@angular/cdk/a11y';
+import { ComponentRef, ElementRef, Renderer2, ViewContainerRef } from '@angular/core';
 import { TestBed } from '@angular/core/testing';
+import { TooltipComponent } from './tooltip.component';
 import { TooltipDirective } from './tooltip.directive';
 
-export class MockElementRef extends ElementRef {}
+class MockElementRef extends ElementRef {
+  nativeElement = {
+    contains: jest.fn(),
+    appendChild: jest.fn(),
+  };
+}
 
 describe('TooltipDirective', () => {
+  let directive: TooltipDirective;
+  const mockComponentRef: ComponentRef<TooltipComponent> = {
+    setInput: jest.fn(),
+    destroy: jest.fn(),
+    onDestroy: jest.fn(),
+    componentType: TooltipComponent,
+    changeDetectorRef: null,
+    location: null,
+    hostView: null,
+    injector: null,
+    instance: { id: '', left: 0, top: 0, text: '' },
+  };
+
   beforeEach((): void => {
     TestBed.configureTestingModule({
-      providers: [ViewContainerRef, { provide: ElementRef, useClass: MockElementRef }],
+      providers: [ViewContainerRef, { provide: ElementRef, useClass: MockElementRef }, Renderer2, InteractivityChecker],
+    });
+    TestBed.runInInjectionContext(() => {
+      directive = new TooltipDirective();
     });
   });
 
-  it('should create an instance', () => {
-    TestBed.runInInjectionContext(() => {
-      const directive: TooltipDirective = new TooltipDirective();
+  it('should create a directive', () => {
+    expect(directive).toBeTruthy();
+  });
 
-      expect(directive).toBeTruthy();
+  describe('ngOnDestroy', () => {
+    it('should destroy tooltip', () => {
+      directive.destroy = jest.fn();
+
+      directive.ngOnDestroy();
+
+      expect(directive.destroy).toHaveBeenCalled();
     });
   });
 
-  describe('ngOnDestroy', () => {
+  describe('createTooltip', () => {
+    beforeEach(() => {
+      directive.viewContainerRef.createComponent = jest.fn().mockReturnValue({ location: { nativeElement: {} } });
+      directive.setDescribedBy = jest.fn();
+      directive.setTooltipProperties = jest.fn();
+    });
+
+    it('should create tooltip component', () => {
+      directive.createTooltip();
+
+      expect(directive.viewContainerRef.createComponent).toHaveBeenCalled();
+    });
+
+    it('should insert tooltip component to parent', () => {
+      directive.createTooltip();
+
+      expect(directive.elementRef.nativeElement.appendChild).toHaveBeenCalled();
+    });
+
+    it('should set aria described by attribute to parent', () => {
+      directive.createTooltip();
+
+      expect(directive.setDescribedBy).toHaveBeenCalled();
+    });
+
+    it('should set tooltip properties', () => {
+      directive.createTooltip();
+
+      expect(directive.setTooltipProperties).toHaveBeenCalled();
+    });
+  });
+
+  describe('destroyTooltip', () => {
     it('should destroy tooltip', () => {
-      TestBed.runInInjectionContext(() => {
-        const directive = new TooltipDirective();
-        directive.destroy = jest.fn();
+      directive.destroy = jest.fn();
+
+      directive.destroyTooltip();
+
+      expect(directive.destroy).toHaveBeenCalled();
+    });
+  });
+
+  describe('onKeydown', () => {
+    it('should destroy tooltip if escape key pressed', () => {
+      directive.destroy = jest.fn();
+      const escapeEvent: KeyboardEvent = { ...new KeyboardEvent('esc'), key: 'Escape' };
+
+      directive.onKeydown(escapeEvent);
 
-        directive.ngOnDestroy();
+      expect(directive.destroy).toHaveBeenCalled();
+    });
+  });
+
+  describe('setTooltipProperties', () => {
+    beforeEach(() => {
+      directive.componentRef = mockComponentRef;
+      directive.elementRef.nativeElement.getBoundingClientRect = jest
+        .fn()
+        .mockReturnValue({ left: 0, right: 1000, bottom: 1000 });
+    });
+
+    it('should get bounding client rect', () => {
+      directive.setTooltipProperties();
 
-        expect(directive.destroy).toHaveBeenCalled();
+      expect(directive.elementRef.nativeElement.getBoundingClientRect).toHaveBeenCalled();
+    });
+
+    it('should set tooltip instance properties', () => {
+      directive.tooltip = 'I am tooltip';
+      directive.tooltipId = 'tooltip-1';
+
+      directive.setTooltipProperties();
+
+      expect(directive.componentRef.instance).toStrictEqual({
+        id: 'tooltip-1',
+        left: 500,
+        text: 'I am tooltip',
+        top: 1000,
       });
     });
+
+    it('should add margin if parent element focused', () => {
+      directive.setTooltipProperties(true);
+
+      expect(directive.componentRef.instance.top).toBe(1004);
+    });
+  });
+
+  describe('setDescribedBy', () => {
+    beforeEach(() => {
+      directive.getFocusableElement = jest.fn();
+      directive.renderer.setAttribute = jest.fn();
+      directive.interactivityChecker.isFocusable = jest.fn();
+    });
+
+    it('should check if parent element focusable', () => {
+      directive.setDescribedBy();
+
+      expect(directive.interactivityChecker.isFocusable).toHaveBeenCalled();
+    });
+
+    it('should get focusable element if parent not focusable', () => {
+      directive.setDescribedBy();
+
+      expect(directive.getFocusableElement).toHaveBeenCalled();
+    });
+
+    it('should set describedby attribute', () => {
+      directive.setDescribedBy();
+
+      expect(directive.renderer.setAttribute).toHaveBeenCalled();
+    });
+  });
+
+  describe('removeDescribedBy', () => {
+    beforeEach(() => {
+      directive.renderer.removeAttribute = jest.fn();
+    });
+
+    it('should remove describedby attribute', () => {
+      directive.removeDescribedBy();
+
+      expect(directive.renderer.removeAttribute).toHaveBeenCalled();
+    });
+  });
+
+  describe('getFocusableElement', () => {
+    it('should return null', () => {
+      const simpleElement = document.createElement('a');
+
+      const result: HTMLElement = directive.getFocusableElement(simpleElement);
+
+      expect(result).toBeNull();
+    });
+
+    it('should return focusable child element', () => {
+      const nestedElement = document.createElement('div');
+      const buttonElement = document.createElement('button');
+      nestedElement.appendChild(buttonElement);
+
+      const result: HTMLElement = directive.getFocusableElement(nestedElement);
+
+      expect(result).toBe(buttonElement);
+    });
+  });
+
+  describe('destroy', () => {
+    beforeEach(() => {
+      directive.componentRef = mockComponentRef;
+      directive.removeDescribedBy = jest.fn();
+    });
+
+    it('should set component ref to null', () => {
+      directive.destroy();
+
+      expect(directive.componentRef).toBeNull();
+    });
+
+    it('should remove describedby attribute', () => {
+      directive.destroy();
+
+      expect(directive.removeDescribedBy).toHaveBeenCalled();
+    });
+
+    it('should set focusable element to null', () => {
+      directive.destroy();
+
+      expect(directive.focusableElement).toBeNull();
+    });
   });
 });
diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts
index 69a4cfe48bc99cf92061ab0eb9d8f361da980029..6af9df79e87cf64bdb3c73867a9a631e1f2b210a 100644
--- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts
+++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts
@@ -1,12 +1,14 @@
+import { isEscapeKey } from '@alfa-client/tech-shared';
+import { InteractivityChecker } from '@angular/cdk/a11y';
 import {
   ComponentRef,
   Directive,
   ElementRef,
-  HostBinding,
   HostListener,
   inject,
   Input,
   OnDestroy,
+  Renderer2,
   ViewContainerRef,
 } from '@angular/core';
 import { uniqueId } from 'lodash-es';
@@ -19,27 +21,28 @@ import { TooltipComponent } from './tooltip.component';
 export class TooltipDirective implements OnDestroy {
   @Input() tooltip: string = '';
 
-  private componentRef: ComponentRef<TooltipComponent> = null;
-  private tooltipId: string;
-  public readonly viewContainerRef: ViewContainerRef = inject(ViewContainerRef);
-  public readonly elementRef: ElementRef<HTMLElement> = inject(ElementRef);
+  componentRef: ComponentRef<TooltipComponent> = null;
+  focusableElement: HTMLElement = null;
+  tooltipId: string;
+
+  public viewContainerRef: ViewContainerRef = inject(ViewContainerRef);
+  public elementRef: ElementRef<HTMLElement> = inject(ElementRef);
+  public renderer: Renderer2 = inject(Renderer2);
+  public interactivityChecker: InteractivityChecker = inject(InteractivityChecker);
 
   ngOnDestroy(): void {
     this.destroy();
   }
 
-  @HostBinding('attr.aria-describedby') get describedBy() {
-    return this.tooltipId;
-  }
-
   @HostListener('mouseenter')
   @HostListener('focusin')
   createTooltip(): void {
     if (this.componentRef === null) {
-      const attachedToFocused: boolean = this.elementRef.nativeElement.contains(document.activeElement);
-      this.tooltipId = uniqueId('tooltip');
+      const nativeElement: HTMLElement = this.elementRef.nativeElement;
+      const attachedToFocused: boolean = nativeElement.contains(document.activeElement);
       this.componentRef = this.viewContainerRef.createComponent(TooltipComponent);
-      this.viewContainerRef.insert(this.componentRef.hostView);
+      nativeElement.appendChild(this.componentRef.location.nativeElement);
+      this.setDescribedBy();
       this.setTooltipProperties(attachedToFocused);
     }
   }
@@ -51,8 +54,9 @@ export class TooltipDirective implements OnDestroy {
     this.destroy();
   }
 
-  @HostListener('keydown', ['$event']) onKeydown(e: KeyboardEvent) {
-    if (this.isEscapeKey(e)) {
+  @HostListener('keydown', ['$event'])
+  onKeydown(e: KeyboardEvent): void {
+    if (isEscapeKey(e)) {
       this.destroy();
     }
   }
@@ -67,15 +71,28 @@ export class TooltipDirective implements OnDestroy {
     }
   }
 
+  setDescribedBy(): void {
+    const nativeElement: HTMLElement = this.elementRef.nativeElement;
+    this.tooltipId = uniqueId('tooltip');
+    this.focusableElement =
+      this.interactivityChecker.isFocusable(nativeElement) ? nativeElement : this.getFocusableElement(nativeElement);
+    this.renderer.setAttribute(this.focusableElement, 'aria-describedby', this.tooltipId);
+  }
+
+  removeDescribedBy(): void {
+    this.renderer.removeAttribute(this.focusableElement, 'aria-describedby');
+  }
+
+  getFocusableElement(element: HTMLElement): HTMLElement {
+    return element.querySelector('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');
+  }
+
   destroy(): void {
     if (this.componentRef !== null) {
       this.componentRef.destroy();
       this.componentRef = null;
-      this.tooltipId = null;
+      this.removeDescribedBy();
+      this.focusableElement = null;
     }
   }
-
-  isEscapeKey(e: KeyboardEvent): boolean {
-    return e.key === 'Escape';
-  }
 }