angular-component
Angular v20以降のベストプラクティスに従い、モダンなスタンドアロンコンポーネントを作成します。シグナルベースのinputs/outputs、OnPush変更検出、ホストバインディング、コンテンツプロジェクション、ライフサイクルフックを活用したUIコンポーネントの構築に使用します。コンポーネントの新規作成、クラスベースinputsのシグナルへのリファクタリング、ホストバインディングの追加、またはアクセシブルなインタラクティブコンポーネントの実装時にトリガーされます。
description の原文を見る
Create modern Angular standalone components following v20+ best practices. Use for building UI components with signal-based inputs/outputs, OnPush change detection, host bindings, content projection, and lifecycle hooks. Triggers on component creation, refactoring class-based inputs to signals, adding host bindings, or implementing accessible interactive components.
SKILL.md 本文
Angular コンポーネント
Angular v20+ 用のスタンドアロンコンポーネントを作成します。コンポーネントはデフォルトでスタンドアロンです。standalone: true を設定してはいけません。
コンポーネント構造
import { Component, ChangeDetectionStrategy, input, output, computed } from '@angular/core';
@Component({
selector: 'app-user-card',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'class': 'user-card',
'[class.active]': 'isActive()',
'(click)': 'handleClick()',
},
template: `
<img [src]="avatarUrl()" [alt]="name() + ' avatar'" />
<h2>{{ name() }}</h2>
@if (showEmail()) {
<p>{{ email() }}</p>
}
`,
styles: `
:host { display: block; }
:host.active { border: 2px solid blue; }
`,
})
export class UserCard {
// Required input
name = input.required<string>();
// Optional input with default
email = input<string>('');
showEmail = input(false);
// Input with transform
isActive = input(false, { transform: booleanAttribute });
// Computed from inputs
avatarUrl = computed(() => `https://api.example.com/avatar/${this.name()}`);
// Output
selected = output<string>();
handleClick() {
this.selected.emit(this.name());
}
}
シグナル入力
// 必須 - 親コンポーネントから提供される必要がある
name = input.required<string>();
// デフォルト値付きのオプション
count = input(0);
// デフォルトなし(undefined が許可される)
label = input<string>();
// テンプレート バインディング用のエイリアス付き
size = input('medium', { alias: 'buttonSize' });
// トランスフォーム関数付き
disabled = input(false, { transform: booleanAttribute });
value = input(0, { transform: numberAttribute });
シグナル出力
import { output, outputFromObservable } from '@angular/core';
// 基本的な出力
clicked = output<void>();
selected = output<Item>();
// エイリアス付き
valueChange = output<number>({ alias: 'change' });
// Observable から(RxJS 相互運用性のため)
scroll$ = new Subject<number>();
scrolled = outputFromObservable(this.scroll$);
// 値を発行
this.clicked.emit();
this.selected.emit(item);
ホストバインディング
@Component の host オブジェクトを使用してください。@HostBinding または @HostListener デコレーターを使用してはいけません。
@Component({
selector: 'app-button',
host: {
// 静的属性
'role': 'button',
// 動的クラスバインディング
'[class.primary]': 'variant() === "primary"',
'[class.disabled]': 'disabled()',
// 動的スタイルバインディング
'[style.--btn-color]': 'color()',
// 属性バインディング
'[attr.aria-disabled]': 'disabled()',
'[attr.tabindex]': 'disabled() ? -1 : 0',
// イベントリスナー
'(click)': 'onClick($event)',
'(keydown.enter)': 'onClick($event)',
'(keydown.space)': 'onClick($event)',
},
template: `<ng-content />`,
})
export class Button {
variant = input<'primary' | 'secondary'>('primary');
disabled = input(false, { transform: booleanAttribute });
color = input('#007bff');
clicked = output<void>();
onClick(event: Event) {
if (!this.disabled()) {
this.clicked.emit();
}
}
}
コンテンツプロジェクション
@Component({
selector: 'app-card',
template: `
<header>
<ng-content select="[card-header]" />
</header>
<main>
<ng-content />
</main>
<footer>
<ng-content select="[card-footer]" />
</footer>
`,
})
export class Card {}
// 使用方法:
// <app-card>
// <h2 card-header>Title</h2>
// <p>Main content</p>
// <button card-footer>Action</button>
// </app-card>
ライフサイクルフック
import { OnDestroy, OnInit, afterNextRender, afterRender } from '@angular/core';
export class My implements OnInit, OnDestroy {
constructor() {
// レンダリング後の DOM 操作用(SSR セーフ)
afterNextRender(() => {
// 最初のレンダリング後に 1 回実行
});
afterRender(() => {
// すべてのレンダリング後に実行
});
}
ngOnInit() { /* コンポーネント初期化 */ }
ngOnDestroy() { /* クリーンアップ */ }
}
アクセシビリティ要件
コンポーネント は以下を満たす必要があります:
- AXE アクセシビリティチェックに合格する
- WCAG AA 標準を満たす
- インタラクティブ要素に適切な ARIA 属性を含める
- キーボード操作をサポートする
- 見える フォーカスインジケーターを維持する
@Component({
selector: 'app-toggle',
host: {
'role': 'switch',
'[attr.aria-checked]': 'checked()',
'[attr.aria-label]': 'label()',
'tabindex': '0',
'(click)': 'toggle()',
'(keydown.enter)': 'toggle()',
'(keydown.space)': 'toggle(); $event.preventDefault()',
},
template: `<span class="toggle-track"><span class="toggle-thumb"></span></span>`,
})
export class Toggle {
label = input.required<string>();
checked = input(false, { transform: booleanAttribute });
checkedChange = output<boolean>();
toggle() {
this.checkedChange.emit(!this.checked());
}
}
テンプレート構文
ネイティブ制御フローを使用してください。*ngIf、*ngFor、*ngSwitch を使用してはいけません。
<!-- 条件分岐 -->
@if (isLoading()) {
<app-spinner />
} @else if (error()) {
<app-error [message]="error()" />
} @else {
<app-content [data]="data()" />
}
<!-- ループ -->
@for (item of items(); track item.id) {
<app-item [item]="item" />
} @empty {
<p>No items found</p>
}
<!-- スイッチ -->
@switch (status()) {
@case ('pending') { <span>Pending</span> }
@case ('active') { <span>Active</span> }
@default { <span>Unknown</span> }
}
クラスとスタイルバインディング
ngClass や ngStyle を使用してはいけません。直接バインディングを使用してください:
<!-- クラスバインディング -->
<div [class.active]="isActive()">Single class</div>
<div [class]="classString()">Class string</div>
<!-- スタイルバインディング -->
<div [style.color]="textColor()">Styled text</div>
<div [style.width.px]="width()">With unit</div>
画像
静的画像には NgOptimizedImage を使用してください:
import { NgOptimizedImage } from '@angular/common';
@Component({
imports: [NgOptimizedImage],
template: `
<img ngSrc="/assets/hero.jpg" width="800" height="600" priority />
<img [ngSrc]="imageUrl()" width="200" height="200" />
`,
})
export class Hero {
imageUrl = input.required<string>();
}
詳細なパターンについては、references/component-patterns.md を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- analogjs
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/analogjs/angular-skills / ライセンス: MIT
関連スキル
nano-banana-2
inference.sh CLIを通じてGoogle Gemini 3.1 Flash Image Preview(Nano Banana 2)で画像を生成します。テキストから画像を生成する機能、画像編集、最大14枚の複数画像入力、Google Searchグラウンディング機能に対応しています。トリガーワード:「nano banana 2」「nanobanana 2」「gemini 3.1 flash image」「gemini 3 1 flash image preview」「google image generation」
octocode-slides
洗練されたマルチファイル形式のHTMLプレゼンテーションを生成します。6段階のフロー(概要 → リサーチ → アウトライン → デザイン → 実装 → レビュー)で構成されています。各スライドは独立したHTMLファイルとなり、iframeで読み込まれます。「スライドを作成してほしい」「プレゼンテーションを作ってほしい」「HTMLスライドを生成してほしい」「デックを構築してほしい」といった依頼や、ノート・ドキュメント・コードを洗練されたプレゼンテーションに変換する際に使用できます。
gpt-image2-ppt
OpenAIのgpt-image-2を使用して、視覚的に優れたPPTスライドを生成します。Spatial Glass、Tech Blue、Editorial Monoなど10種類のキュレーション済みスタイルに対応し、ユーザーが提供したPPTXファイルを模倣するテンプレートクローンモードも搭載しています。HTMLビューアと16:9形式のPPTXファイルを出力します。プレゼンテーション、スライド、ピッチデック、投資家向けPPT、雑誌風PPTの作成依頼などで活用してください。
nano-banana
Nano Banana PRO(Gemini 3 Pro Image)およびNano Banana(Gemini 2.5 Flash Image)を使用したAI画像生成機能です。以下の場合に活用できます:(1)テキストプロンプトからの画像生成、(2)既存画像の編集、(3)インフォグラフィックス、ロゴ、商品写真、ステッカーなどのプロフェッショナルなビジュアルアセット制作、(4)複数画像での人物キャラクターの一貫性保持、(5)正確なテキスト描画を含む画像生成、(6)AI生成ビジュアルが必要なあらゆるタスク。「画像を生成」「画像を作成」「写真を作る」「ロゴをデザイン」「インフォグラフィックスを作成」「AI画像」「nano banana」またはその他の画像生成リクエストをトリガーとして機能します。
oiloil-ui-ux-guide
モダンでクリーンなUI/UXガイダンス・レビュースキルです。新機能や既存システム(Webアプリ)に対して、実行可能なUI/UX改善提案、デザイン原則、デザインレビューチェックリストが必要な場合に活用できます。CRAP(コントラスト・反復・配置・近接)をベースに、タスクファーストなUX、情報設計、フィードバック・システムステータス、一貫性、affordances、エラー防止・復旧、認知負荷を重視します。モダンミニマルスタイル(クリーン・余白・タイポグラフィ主導)を強制し、不要なテキストを削減、アイコンとしての絵文字を禁止し、統一されたアイコンセットから直感的で洗練されたアイコンを推奨します。
axiom-hig-ref
Apple Human Interface Guidelines リファレンス — 色(セマンティックカラー、カスタムカラー、パターン)、背景(マテリアル階層、ダイナミック背景)、タイポグラフィ(標準スタイル、カスタムフォント、Dynamic Type)、SF Symbols(レンダリングモード、色、多言語対応)、ダークモード、アクセシビリティ、プラットフォーム固有の考慮事項を網羅したガイドラインです。