Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create components using Angular CDK utilities including drag-drop, overlay, portal, scrolling, a11y, clipboard, and platform detection for ng-events project
.claude/skills/aiskillstore-angular-cdk-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-18 | ✓→✗ | ▼ Worse | 110% | 0% |
| case-13 | ✓→✓ | = Same ✓ | 153% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 267% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 436% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 490% | 0% |
This skill guides the creation of components and features using Angular CDK (@angular/cdk) utilities in the ng-events construction site management system.
Triggers: "Angular CDK", "drag and drop", "overlay", "portal", "virtual scroll", "accessibility", "clipboard", "platform detection", "CDK utilities"
Use this skill when:
Purpose: Create draggable elements and drop zones
typescriptimport { Component } from '@angular/core'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { CommonModule } from '@angular/common'; import { CdkDropList, CdkDrag } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-task-board', standalone: true, imports: [CommonModule, CdkDropList, CdkDrag], template: ` <div class="task-board"> <div class="column" cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[inProgressList, doneList]" (cdkDropListDropped)="drop($event)"> <h3>To Do</h3> @for (task of todo; track task.id) { <div cdkDrag class="task-card"> {{ task.title }} <div *cdkDragPlaceholder class="drag-placeholder"></div> </div> } </div> <div class="column" cdkDropList #inProgressList="cdkDropList" [cdkDropListData]="inProgress" [cdkDropListConnectedTo]="[todoList, doneList]" (cdkDropListDropped)="drop($event)"> <h3>In Progress</h3> @for (task of inProgress; track task.id) { <div cdkDrag class="task-card">{{ task.title }}</div> } </div> <div class="column" cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList, inProgressList]" (cdkDropListDropped)="drop($event)"> <h3>Done</h3> @for (task of done; track task.id) { <div cdkDrag class="task-card">{{ task.title }}</div> } </div> </div> `, styles: [` .task-board { display: flex; gap: 20px; } .column { flex: 1; min-height: 400px; background: #f5f5f5; padding: 16px; border-radius: 4px; } .task-card { background: white; padding: 12px; margin-bottom: 8px; border-radius: 4px; cursor: move; } .task-card:active { box-shadow: 0 5px 5px -3px rgba(0,0,0,.2); } .drag-placeholder { background: #ccc; border: dotted 2px #999; height: 40px; transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } `] }) export class TaskBoardComponent { todo = [ { id: '1', title: 'Task 1' }, { id: '2', title: 'Task 2' } ]; inProgress = [ { id: '3', title: 'Task 3' } ]; done = [ { id: '4', title: 'Task 4' } ]; drop(event: CdkDragDrop<Task[]>) { 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 ); } } }
Purpose: Create floating panels and popups
typescriptimport { Component, inject } from '@angular/core'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; @Component({ selector: 'app-menu-trigger', standalone: true, template: ` <button (click)="openMenu()">Open Menu</button> ` }) export class MenuTriggerComponent { private overlay = inject(Overlay); private overlayRef: OverlayRef | null = null; openMenu(): void { if (this.overlayRef) { this.overlayRef.dispose(); this.overlayRef = null; return; } const positionStrategy = this.overlay .position() .flexibleConnectedTo(this.elementRef) .withPositions([ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' } ]); this.overlayRef = this.overlay.create({ positionStrategy, scrollStrategy: this.overlay.scrollStrategies.reposition(), hasBackdrop: true, backdropClass: 'cdk-overlay-transparent-backdrop' }); const menuPortal = new ComponentPortal(MenuComponent); this.overlayRef.attach(menuPortal); // Close on backdrop click this.overlayRef.backdropClick().subscribe(() => { this.overlayRef?.dispose(); this.overlayRef = null; }); } }
Purpose: Efficiently render large lists
typescriptimport { Component, signal } from '@angular/core'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { CommonModule } from '@angular/common'; @Component({ selector: 'app-task-list-virtual', standalone: true, imports: [CommonModule, ScrollingModule], template: ` <cdk-virtual-scroll-viewport itemSize="50" class="viewport"> <div *cdkVirtualFor="let task of tasks()" class="task-item"> <h4>{{ task.title }}</h4> <p>{{ task.description }}</p> </div> </cdk-virtual-scroll-viewport> `, styles: [` .viewport { height: 400px; width: 100%; border: 1px solid #ccc; } .task-item { height: 50px; padding: 10px; border-bottom: 1px solid #eee; } `] }) export class TaskListVirtualComponent { tasks = signal(Array.from({ length: 10000 }, (_, i) => ({ id: `task-${i}`, title: `Task ${i}`, description: `Description for task ${i}` }))); }
Purpose: Copy text to clipboard
typescriptimport { Component, inject } from '@angular/core'; import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'; @Component({ selector: 'app-share-link', standalone: true, imports: [ClipboardModule], template: ` <div class="share-container"> <input [value]="shareLink" readonly #linkInput /> <button [cdkCopyToClipboard]="shareLink" (cdkCopyToClipboardCopied)="onCopied($event)"> Copy Link </button> @if (copied) { <span class="success">Copied!</span> } </div> ` }) export class ShareLinkComponent { private clipboard = inject(Clipboard); shareLink = 'https://ng-events.com/blueprints/123'; copied = false; onCopied(success: boolean): void { if (success) { this.copied = true; setTimeout(() => this.copied = false, 2000); } } }
Purpose: Detect browser and platform capabilities
typescriptimport { Component, inject, OnInit } from '@angular/core'; import { Platform } from '@angular/cdk/platform'; @Component({ selector: 'app-platform-aware', standalone: true, template: ` <div class="platform-info"> <h3>Platform Information</h3> <ul> <li>Browser: {{ browser }}</li> <li>Mobile: {{ isMobile }}</li> <li>iOS: {{ isIOS }}</li> <li>Android: {{ isAndroid }}</li> </ul> </div> ` }) export class PlatformAwareComponent implements OnInit { private platform = inject(Platform); browser = ''; isMobile = false; isIOS = false; isAndroid = false; ngOnInit(): void { this.isMobile = this.platform.IOS || this.platform.ANDROID; this.isIOS = this.platform.IOS; this.isAndroid = this.platform.ANDROID; if (this.platform.FIREFOX) this.browser = 'Firefox'; else if (this.platform.EDGE) this.browser = 'Edge'; else if (this.platform.SAFARI) this.browser = 'Safari'; else if (this.platform.WEBKIT) this.browser = 'WebKit'; else this.browser = 'Unknown'; } }
When using Angular CDK:
Version: 1.0.0 Compatible with: @angular/cdk 20.x, Angular 20.3.x Last Updated: 2025-12-25
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-13 | pass→pass | 11,915 | 10,202 | -14% | 1 | 1 | 0% | 1,768 | 4,481 | +153% | 0 | 0 | — |
case-18 | pass→fail | 12,883 | 12,707 | -1% | 1 | 1 | 0% | 2,510 | 5,266 | +110% | 0 | 0 | — |
case-01 | pass→pass | 5,368 | 4,717 | -12% | 1 | 1 | 0% | 1,025 | 3,760 | +267% | 0 | 0 | — |
case-02 | pass→pass | 3,431 | 3,586 | +5% | 1 | 1 | 0% | 642 | 3,442 | +436% | 0 | 0 | — |
case-03 | pass→pass | 3,501 | 3,389 | -3% | 1 | 1 | 0% | 576 | 3,397 | +490% | 0 | 0 | — |
case-04 | pass→pass | 11,826 | 10,267 | -13% | 1 | 1 | 0% | 2,172 | 4,684 | +116% | 0 | 0 | — |
case-05 | pass→pass | 5,545 | 4,845 | -13% | 1 | 1 | 0% | 904 | 3,533 | +291% | 0 | 0 | — |
case-06 | pass→pass | 3,266 | 3,960 | +21% | 1 | 1 | 0% | 496 | 3,571 | +620% | 0 | 0 | — |
case-07 | pass→pass | 3,613 | 2,523 | -30% | 1 | 1 | 0% | 558 | 3,204 | +474% | 0 | 0 | — |
case-08 | pass→pass | 3,821 | 3,075 | -20% | 1 | 1 | 0% | 742 | 3,350 | +351% | 0 | 0 | — |
case-09 | pass→pass | 3,885 | 2,784 | -28% | 1 | 1 | 0% | 613 | 3,351 | +447% | 0 | 0 | — |
case-10 | pass→pass | 11,858 | 8,028 | -32% | 1 | 1 | 0% | 1,967 | 4,234 | +115% | 0 | 0 | — |
case-11 | pass→pass | 12,757 | 9,292 | -27% | 1 | 1 | 0% | 2,141 | 4,448 | +108% | 0 | 0 | — |
case-12 | pass→pass | 5,221 | 4,122 | -21% | 1 | 1 | 0% | 801 | 3,512 | +338% | 0 | 0 | — |
case-14 | pass→pass | 2,550 | 4,210 | +65% | 1 | 1 | 0% | 433 | 3,410 | +688% | 0 | 0 | — |
case-15 | pass→pass | 4,929 | 3,558 | -28% | 1 | 1 | 0% | 535 | 3,379 | +532% | 0 | 0 | — |
case-16 | pass→pass | 10,296 | 8,082 | -22% | 1 | 1 | 0% | 1,979 | 4,368 | +121% | 0 | 0 | — |
case-17 | pass→pass | 6,406 | 4,096 | -36% | 1 | 1 | 0% | 701 | 3,560 | +408% | 0 | 0 | — |
case-19 | pass→pass | 5,942 | 7,632 | +28% | 1 | 1 | 0% | 1,064 | 3,686 | +246% | 0 | 0 | — |
case-20 | fail→fail | 9,761 | 7,273 | -25% | 1 | 1 | 0% | 1,663 | 4,007 | +141% | 0 | 0 | — |
case-21 | pass→pass | 10,475 | 11,284 | +8% | 1 | 1 | 0% | 1,924 | 4,975 | +159% | 0 | 0 | — |
case-22 | pass→pass | 3,377 | 2,895 | -14% | 1 | 1 | 0% | 594 | 3,276 | +452% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of -100 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Other measured skills in the registry, with their headline benchmark lift.