jpskill.com
🛠️ 開発・MCP コミュニティ

angular-cdk

Angular CDKを活用し、アクセシビリティや仮想スクロール、レイアウトなどに対応したカスタムUIコンポーネントを開発するSkill。

📜 元の英語説明(参考)

Angular Component Dev Kit (CDK) expertise for building custom components with accessibility, overlays, drag-and-drop, virtual scrolling, and layout utilities. Use when creating custom UI components, implementing accessibility features, building overlays/dialogs, virtual scrolling for large lists, or using CDK utilities for responsive layouts.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Angular CDKを活用し、アクセシビリティや仮想スクロール、レイアウトなどに対応したカスタムUIコンポーネントを開発するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o angular-cdk.zip https://jpskill.com/download/6828.zip && unzip -o angular-cdk.zip && rm angular-cdk.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/6828.zip -OutFile "$d\angular-cdk.zip"; Expand-Archive "$d\angular-cdk.zip" -DestinationPath $d -Force; ri "$d\angular-cdk.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して angular-cdk.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → angular-cdk フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-17
取得日時
2026-05-17
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

[Skill 名] angular-cdk

Angular CDK (Component Dev Kit) スキル

ルール

ライフサイクルとリソース管理

  • ngOnInit()focusTrapFactory.create() を使用して FocusTrap を作成する必要があります。
  • フォーカストラップの作成後、focusTrap.focusInitialElement() を呼び出す必要があります。
  • ngOnDestroy()focusTrap.destroy() を使用して FocusTrap を破棄する必要があります。
  • ngOnDestroy()focusMonitor.stopMonitoring() を使用してフォーカス監視を停止する必要があります。
  • ngOnDestroy()keyManager.destroy() を使用して keyManager を破棄する必要があります。
  • クリーンアップ時に overlayRef.dispose() を使用して overlayRef を破棄する必要があります。
  • ngOnDestroy() で全ての CDK リソースを明示的に解放する必要があります。
  • focusTrapoverlayReffocusMonitorkeyManager をクリーンアップする必要があります。
  • CDK リソースをクリーンアップせずに放置してはいけません(メモリリークの原因となります)。

アクセシビリティ

  • 動的コンテンツのスクリーンリーダーアナウンスには LiveAnnouncer を使用する必要があります。
  • リストのキーボードナビゲーションには FocusKeyManager を使用する必要があります。

オーバーレイの配置

  • 少なくとも2つのフォールバック位置戦略を持つ flexibleConnectedTo() を使用する必要があります。
  • オーバーレイには明示的な scrollStrategy を設定する必要があります。
  • オーバーレイには明示的な hasBackdrop プロパティを設定する必要があります。
  • overlayRef.attach(portal) を使用してポータルをオーバーレイにアタッチする必要があります。

ポータルの使用

  • 動的なコンポーネントレンダリングには ComponentPortal を使用する必要があります。
  • テンプレートコンテンツのプロジェクションには TemplatePortal を使用する必要があります。
  • メモリリークを防ぐため、不要になったらポータルをデタッチする必要があります。

ドラッグ&ドロップ

  • (cdkDropListDropped) イベントハンドラを持つ cdkDropList コンテナを使用する必要があります。
  • ドラッグ可能なアイテムには cdkDrag ディレクティブを使用する必要があります。
  • ドラッグアイテムを含む @for ループでは track 式を使用する必要があります。
  • 単一リストの並べ替えには moveItemInArray() を使用する必要があります。
  • リスト間の転送には transferArrayItem() を使用する必要があります。
  • リスト間のドラッグには [cdkDropListConnectedTo] を使用してドロップリストを接続する必要があります。

仮想スクロール

  • 100アイテムを超えるリストにのみ CdkVirtualScrollViewport を使用してください。
  • 明示的な itemSize プロパティを設定する必要があります。
  • スムーズなスクロールのために minBufferPxmaxBufferPx を設定する必要があります。
  • ビューポートコンテナに固定の高さを設定する必要があります。
  • 自動高さの仮想スクロールビューポートを使用してはいけません。

ブレークポイントオブザーバー

  • レスポンシブレイアウトの検出には BreakpointObserver を使用する必要があります。
  • ブレークポイントのオブザーバブルをシグナルに変換する際には toSignal() を使用する必要があります。
  • ブレークポイントで toSignal() を使用する際には initialValue を提供する必要があります。
  • window.innerWidth の直接使用や手動のリサイズリスナーを使用してはいけません。
  • window.matchMedia() の直接呼び出しを使用してはいけません。

コンテキスト

🎯 目的

このスキルは、Material Design のスタイルなしで、アクセシブルで高品質な Angular コンポーネントを構築するための動作プリミティブのセットである Angular Component Dev Kit (CDK) に関する包括的なガイダンスを提供します。

📦 Angular CDK とは?

Angular CDK は、スタイルなしの UI コンポーネントの動作ライブラリです。

  • アクセシビリティ (A11y): スクリーンリーダーサポート、キーボードナビゲーション、フォーカス管理
  • オーバーレイ: 配置、背景、オーバーレイ管理
  • ポータル: 動的なコンポーネントレンダリング
  • ドラッグ&ドロップ: ソート可能なリストとドラッグ&ドロップインタラクション
  • 仮想スクロール: 大規模なリストの効率的なレンダリング
  • レイアウト: レスポンシブレイアウトユーティリティとブレークポイントオブザーバー
  • テーブル: スタイルなしのデータテーブル機能
  • ツリー: 階層的なツリー構造

🎨 このスキルを使用するタイミング

以下の状況で Angular CDK のガイダンスを使用してください。

  • ゼロからカスタム UI コンポーネントを構築する場合
  • アクセシブルなコンポーネント(ARIA、フォーカス管理)を実装する場合
  • オーバーレイ、モーダル、またはツールチップを作成する場合
  • ドラッグ&ドロップ機能を構築する場合
  • パフォーマンスのために仮想スクロールを実装する場合
  • レスポンシブレイアウトとブレークポイントを扱う場合
  • カスタムデータテーブルやツリービューを作成する場合
  • フォーカストラップとキーボードナビゲーションを管理する場合

📚 コア CDK モジュール

1. アクセシビリティ (A11y)

フォーカス管理:

import { FocusTrapFactory, FocusMonitor } from '@angular/cdk/a11y';

export class DialogComponent implements OnInit, OnDestroy {
  private focusTrap: FocusTrap;

  constructor(
    private focusTrapFactory: FocusTrapFactory,
    private focusMonitor: FocusMonitor,
    private elementRef: ElementRef
  ) {}

  ngOnInit() {
    // ダイアログ内にフォーカスを保持するためのフォーカストラップを作成
    this.focusTrap = this.focusTrapFactory.create(
      this.elementRef.nativeElement
    );
    this.focusTrap.focusInitialElement();

    // フォーカス変更を監視
    this.focusMonitor.monitor(this.elementRef, true).subscribe(origin => {
      console.log('Focus origin:', origin);
    });
  }

  ngOnDestroy() {
    this.focusTrap.destroy();
    this.focusMonitor.stopMonitoring(this.elementRef);
  }
}

ライブアナウンサー (スクリーンリーダー):

import { LiveAnnouncer } from '@angular/cdk/a11y';

export class NotificationComponent {
  constructor(private liveAnnouncer: LiveAnnouncer) {}

  announce(message: string) {
    // スクリーンリーダーにアナウンス
    this.liveAnnouncer.announce(message, 'polite');
  }

  announceUrgent(message: string) {
    this.liveAnnouncer.announce(message, 'assertive');
  }
}

リストキーマネージャー (キーボードナビゲーション):

import { FocusKeyManager } from '@angular/cdk/a11y';
import { QueryList, ViewChildren, AfterViewInit } from '@angular/core';

export class MenuComponent implements AfterViewInit {
  @ViewChildren(MenuItem) menuItems: QueryList<MenuItem>;
  private keyManager: FocusKeyManager<MenuItem>;

  ngAfterViewInit() {
    this.keyManager = new FocusKeyManager(this.menuItems)
      .withWrap()
      .withVerticalOrientation();
  }

  onKeydown(event: KeyboardEvent) {
    this.keyManager.onKeydown(event);
  }
}

2. オーバーレイ

オーバーレイの作成:

import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';

export class TooltipDirective {
  private overlayRef: OverlayRef | null = null;
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Angular CDK (Component Dev Kit) Skill

Rules

Lifecycle & Resource Management

  • Must create FocusTrap in ngOnInit() using focusTrapFactory.create()
  • Must call focusTrap.focusInitialElement() after creating focus trap
  • Must destroy FocusTrap in ngOnDestroy() using focusTrap.destroy()
  • Must stop monitoring focus in ngOnDestroy() using focusMonitor.stopMonitoring()
  • Must destroy keyManager in ngOnDestroy() using keyManager.destroy()
  • Must dispose overlayRef in cleanup using overlayRef.dispose()
  • Must explicitly release all CDK resources in ngOnDestroy()
  • Must clean up: focusTrap, overlayRef, focusMonitor, keyManager
  • Shall Not leave CDK resources without cleanup (causes memory leaks)

Accessibility

  • Must use LiveAnnouncer for screen reader announcements of dynamic content
  • Must use FocusKeyManager for keyboard navigation in lists

Overlay Positioning

  • Must use flexibleConnectedTo() with at least two fallback position strategies
  • Must configure explicit scrollStrategy for overlay
  • Must configure explicit hasBackdrop property for overlay
  • Must attach portal to overlay using overlayRef.attach(portal)

Portal Usage

  • Must use ComponentPortal for dynamic component rendering
  • Must use TemplatePortal for template content projection
  • Must detach portal when no longer needed to prevent memory leaks

Drag & Drop

  • Must use cdkDropList container with (cdkDropListDropped) event handler
  • Must use cdkDrag directive on draggable items
  • Must use track expressions in @for loops with drag items
  • Must use moveItemInArray() for single-list reordering
  • Must use transferArrayItem() for cross-list transfers
  • Must connect drop lists using [cdkDropListConnectedTo] for cross-list drag

Virtual Scrolling

  • Only use CdkVirtualScrollViewport for lists with more than 100 items
  • Must set explicit itemSize property
  • Must configure minBufferPx and maxBufferPx for smooth scrolling
  • Must set fixed height on viewport container
  • Must Not use auto-height virtual scroll viewports

Breakpoint Observer

  • Must use BreakpointObserver for responsive layout detection
  • Must use toSignal() when converting breakpoint observables to signals
  • Must provide initialValue when using toSignal() with breakpoints
  • Must Not use direct window.innerWidth or manual resize listeners
  • Must Not use direct window.matchMedia() calls

Context

🎯 Purpose

This skill provides comprehensive guidance on the Angular Component Dev Kit (CDK), a set of behavior primitives for building accessible, high-quality Angular components without Material Design styling.

📦 What is Angular CDK?

The Angular CDK is a library of unstyled UI component behaviors:

  • Accessibility (A11y): Screen reader support, keyboard navigation, focus management
  • Overlay: Positioning, backdrop, and overlay management
  • Portal: Dynamic component rendering
  • Drag & Drop: Sortable lists and drag-and-drop interactions
  • Virtual Scrolling: Efficient rendering of large lists
  • Layout: Responsive layout utilities and breakpoint observers
  • Table: Data table functionality without styling
  • Tree: Hierarchical tree structures

🎨 When to Use This Skill

Use Angular CDK guidance when:

  • Building custom UI components from scratch
  • Implementing accessible components (ARIA, focus management)
  • Creating overlays, modals, or tooltips
  • Building drag-and-drop functionality
  • Implementing virtual scrolling for performance
  • Working with responsive layouts and breakpoints
  • Creating custom data tables or tree views
  • Managing focus trapping and keyboard navigation

📚 Core CDK Modules

1. Accessibility (A11y)

Focus Management:

import { FocusTrapFactory, FocusMonitor } from '@angular/cdk/a11y';

export class DialogComponent implements OnInit, OnDestroy {
  private focusTrap: FocusTrap;

  constructor(
    private focusTrapFactory: FocusTrapFactory,
    private focusMonitor: FocusMonitor,
    private elementRef: ElementRef
  ) {}

  ngOnInit() {
    // Create focus trap to keep focus within dialog
    this.focusTrap = this.focusTrapFactory.create(
      this.elementRef.nativeElement
    );
    this.focusTrap.focusInitialElement();

    // Monitor focus changes
    this.focusMonitor.monitor(this.elementRef, true).subscribe(origin => {
      console.log('Focus origin:', origin);
    });
  }

  ngOnDestroy() {
    this.focusTrap.destroy();
    this.focusMonitor.stopMonitoring(this.elementRef);
  }
}

Live Announcer (Screen Readers):

import { LiveAnnouncer } from '@angular/cdk/a11y';

export class NotificationComponent {
  constructor(private liveAnnouncer: LiveAnnouncer) {}

  announce(message: string) {
    // Announce to screen readers
    this.liveAnnouncer.announce(message, 'polite');
  }

  announceUrgent(message: string) {
    this.liveAnnouncer.announce(message, 'assertive');
  }
}

List Key Manager (Keyboard Navigation):

import { FocusKeyManager } from '@angular/cdk/a11y';
import { QueryList, ViewChildren, AfterViewInit } from '@angular/core';

export class MenuComponent implements AfterViewInit {
  @ViewChildren(MenuItem) menuItems: QueryList<MenuItem>;
  private keyManager: FocusKeyManager<MenuItem>;

  ngAfterViewInit() {
    this.keyManager = new FocusKeyManager(this.menuItems)
      .withWrap()
      .withVerticalOrientation();
  }

  onKeydown(event: KeyboardEvent) {
    this.keyManager.onKeydown(event);
  }
}

2. Overlay

Creating Overlays:

import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';

export class TooltipDirective {
  private overlayRef: OverlayRef | null = null;

  constructor(
    private overlay: Overlay,
    private elementRef: ElementRef
  ) {}

  show() {
    // Create overlay configuration
    const config = new OverlayConfig({
      positionStrategy: this.overlay.position()
        .flexibleConnectedTo(this.elementRef)
        .withPositions([
          {
            originX: 'center',
            originY: 'bottom',
            overlayX: 'center',
            overlayY: 'top',
            offsetY: 8
          }
        ]),
      hasBackdrop: false,
      scrollStrategy: this.overlay.scrollStrategies.reposition()
    });

    // Create overlay
    this.overlayRef = this.overlay.create(config);

    // Attach component to overlay
    const portal = new ComponentPortal(TooltipComponent);
    this.overlayRef.attach(portal);
  }

  hide() {
    if (this.overlayRef) {
      this.overlayRef.dispose();
      this.overlayRef = null;
    }
  }
}

Global Position Strategy:

const config = new OverlayConfig({
  positionStrategy: this.overlay.position()
    .global()
    .centerHorizontally()
    .centerVertically(),
  hasBackdrop: true,
  backdropClass: 'dark-backdrop'
});

3. Portal

Template Portals:

import { TemplatePortal } from '@angular/cdk/portal';
import { TemplateRef, ViewContainerRef } from '@angular/core';

export class PortalExample {
  @ViewChild('templatePortalContent') templateRef: TemplateRef<any>;
  selectedPortal: TemplatePortal<any>;

  constructor(private viewContainerRef: ViewContainerRef) {}

  createTemplatePortal() {
    this.selectedPortal = new TemplatePortal(
      this.templateRef,
      this.viewContainerRef
    );
  }
}
<ng-template #templatePortalContent>
  <p>This content will be rendered in a portal</p>
</ng-template>

<div cdkPortalOutlet [cdkPortalOutlet]="selectedPortal"></div>

Component Portals:

import { ComponentPortal } from '@angular/cdk/portal';

createComponentPortal() {
  const portal = new ComponentPortal(
    MyComponent,
    null, // ViewContainerRef (optional)
    this.injector, // Custom injector (optional)
    this.componentFactoryResolver // (optional)
  );

  // Attach to overlay or portal outlet
  const ref = this.overlayRef.attach(portal);
  ref.instance.data = 'Custom data';
}

4. Drag & Drop

Sortable List:

import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  template: `
    <div cdkDropList (cdkDropListDropped)="drop($event)">
      @for (item of items; track item) {
        <div cdkDrag>
          <div class="drag-handle" cdkDragHandle>⋮⋮</div>
          {{ item }}
        </div>
      }
    </div>
  `
})
export class SortableListComponent {
  items = ['Item 1', 'Item 2', 'Item 3'];

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
}

Drag Between Lists:

import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';

@Component({
  template: `
    <div class="lists-container">
      <div cdkDropList #todoList="cdkDropList"
           [cdkDropListData]="todo"
           [cdkDropListConnectedTo]="[doneList]"
           (cdkDropListDropped)="drop($event)">
        @for (item of todo; track item) {
          <div cdkDrag>{{ item }}</div>
        }
      </div>

      <div cdkDropList #doneList="cdkDropList"
           [cdkDropListData]="done"
           [cdkDropListConnectedTo]="[todoList]"
           (cdkDropListDropped)="drop($event)">
        @for (item of done; track item) {
          <div cdkDrag>{{ item }}</div>
        }
      </div>
    </div>
  `
})
export class TaskBoardComponent {
  todo = ['Task 1', 'Task 2'];
  done = ['Task 3'];

  drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }
  }
}

5. Virtual Scrolling

Virtual Scroll for Large Lists:

import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';

@Component({
  template: `
    <cdk-virtual-scroll-viewport itemSize="50" class="viewport">
      @for (item of items; track item) {
        <div class="item">{{ item }}</div>
      }
    </cdk-virtual-scroll-viewport>
  `,
  styles: [`
    .viewport {
      height: 400px;
      width: 100%;
    }

    .item {
      height: 50px;
      display: flex;
      align-items: center;
    }
  `]
})
export class VirtualScrollComponent {
  items = Array.from({ length: 10000 }, (_, i) => `Item #${i}`);
}

Dynamic Item Sizes:

@Component({
  template: `
    <cdk-virtual-scroll-viewport 
      [itemSize]="50" 
      minBufferPx="200" 
      maxBufferPx="400">
      @for (item of items; track item.id) {
        <div class="item" [style.height.px]="item.height">
          {{ item.content }}
        </div>
      }
    </cdk-virtual-scroll-viewport>
  `
})
export class DynamicVirtualScrollComponent {
  items = Array.from({ length: 1000 }, (_, i) => ({
    id: i,
    content: `Item ${i}`,
    height: 40 + (i % 3) * 20 // Variable heights
  }));
}

6. Layout & Breakpoints

Breakpoint Observer:

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';

export class ResponsiveComponent {
  isHandset$ = this.breakpointObserver.observe(Breakpoints.Handset)
    .pipe(map(result => result.matches));

  constructor(private breakpointObserver: BreakpointObserver) {
    // Observe multiple breakpoints
    this.breakpointObserver.observe([
      Breakpoints.XSmall,
      Breakpoints.Small,
      Breakpoints.Medium,
      Breakpoints.Large,
      Breakpoints.XLarge
    ]).subscribe(result => {
      if (result.breakpoints[Breakpoints.XSmall]) {
        this.columns = 1;
      } else if (result.breakpoints[Breakpoints.Small]) {
        this.columns = 2;
      } else {
        this.columns = 4;
      }
    });
  }
}

Custom Breakpoints:

const customBreakpoints = {
  mobile: '(max-width: 599px)',
  tablet: '(min-width: 600px) and (max-width: 959px)',
  desktop: '(min-width: 960px)'
};

this.breakpointObserver.observe([customBreakpoints.mobile])
  .subscribe(result => {
    this.isMobile = result.matches;
  });

7. Table

CDK Table (Unstyled):

import { DataSource } from '@angular/cdk/collections';

@Component({
  template: `
    <cdk-table [dataSource]="dataSource">
      <ng-container cdkColumnDef="name">
        <cdk-header-cell *cdkHeaderCellDef>Name</cdk-header-cell>
        <cdk-cell *cdkCellDef="let row">{{ row.name }}</cdk-cell>
      </ng-container>

      <ng-container cdkColumnDef="email">
        <cdk-header-cell *cdkHeaderCellDef>Email</cdk-header-cell>
        <cdk-cell *cdkCellDef="let row">{{ row.email }}</cdk-cell>
      </ng-container>

      <cdk-header-row *cdkHeaderRowDef="displayedColumns"></cdk-header-row>
      <cdk-row *cdkRowDef="let row; columns: displayedColumns;"></cdk-row>
    </cdk-table>
  `
})
export class TableComponent {
  displayedColumns = ['name', 'email'];
  dataSource = new MyDataSource();
}

8. Tree

Nested Tree:

import { NestedTreeControl } from '@angular/cdk/tree';
import { ArrayDataSource } from '@angular/cdk/collections';

interface FileNode {
  name: string;
  children?: FileNode[];
}

@Component({
  template: `
    <cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
      <cdk-nested-tree-node *cdkTreeNodeDef="let node">
        {{ node.name }}
      </cdk-nested-tree-node>

      <cdk-nested-tree-node *cdkTreeNodeDef="let node; when: hasChild">
        <button (click)="treeControl.toggle(node)">
          {{ treeControl.isExpanded(node) ? '▼' : '▶' }}
        </button>
        {{ node.name }}

        <div *ngIf="treeControl.isExpanded(node)">
          <ng-container cdkTreeNodeOutlet></ng-container>
        </div>
      </cdk-nested-tree-node>
    </cdk-tree>
  `
})
export class TreeComponent {
  treeControl = new NestedTreeControl<FileNode>(node => node.children);
  dataSource = new ArrayDataSource(TREE_DATA);

  hasChild = (_: number, node: FileNode) => !!node.children && node.children.length > 0;
}

🎯 Best Practices

1. Accessibility First

// Always use CDK a11y utilities for custom components
import { FocusTrap, LiveAnnouncer } from '@angular/cdk/a11y';

// Trap focus in modals
// Announce dynamic content changes
// Implement keyboard navigation

2. Overlay Positioning

// Use flexible positioning for better UX
const positionStrategy = this.overlay.position()
  .flexibleConnectedTo(this.elementRef)
  .withPositions([
    // Primary position
    { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
    // Fallback positions
    { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },
    { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top' }
  ]);

3. Virtual Scrolling Performance

// Configure buffer sizes for smooth scrolling
<cdk-virtual-scroll-viewport 
  itemSize="50"
  minBufferPx="400"   // Render extra items above
  maxBufferPx="800">  // Render extra items below

4. Drag & Drop Constraints

// Constrain drag movement
<div cdkDrag [cdkDragBoundary]="'.boundary-element'">
  Constrained drag
</div>

// Custom drag preview
<div cdkDrag>
  <div *cdkDragPreview>Custom preview</div>
  <div *cdkDragPlaceholder>Placeholder</div>
  Content
</div>

🐛 Troubleshooting

Issue Solution
Overlay not positioning correctly Check scroll strategy and position preferences
Virtual scroll not rendering Ensure itemSize matches actual item height
Drag & drop not working Import DragDropModule and use correct directives
Focus trap not working Ensure element has focusable children
Breakpoint observer not triggering Check for correct breakpoint strings
Tree not expanding Implement hasChild function and tree control

📖 References

💡 Common Use Cases

  1. Custom Dropdown: Use Overlay + Portal
  2. Accessible Dialog: Use Overlay + Focus Trap + Live Announcer
  3. Infinite Scroll: Use Virtual Scroll Viewport
  4. Sortable Table: Use CDK Table + Drag Drop
  5. Responsive Sidebar: Use Breakpoint Observer + Layout
  6. File Tree: Use CDK Tree
  7. Custom Tooltip: Use Overlay + Positioning Strategy

📂 Recommended Placement

Project-level skill:

/.github/skills/angular-cdk/SKILL.md

Copilot will load this when working with Angular CDK features.