Angular Master Component
Descrição
Skill completa para criar componentes Angular com HTML, SCSS e TS seguindo o padrão do projeto.
Quando Usar
- Criar novos componentes completos
- Implementar interface de componente do zero
- Quando precisar dos 3 arquivos (TS + HTML + SCSS)
Fluxo Obrigatório
- Ler regras (global e local)
- Ler ANGULAR_CONVENTIONS.MD (se existir)
- Analisar padrão existente do projeto
- Verificar se já existe componente similar reutilizável
- Definir tipo (Smart/Dumb)
- Implementar os 3 arquivos seguindo padrão
- Ler regras na pasta rules(global e ou local) e seguir com muita seriedade,
Estrutura do Componente
nome-feature/
nome-feature.component.ts
nome-feature.component.html
nome-feature.component.scss
1. TypeScript (.component.ts)
Regras Obrigatórias
import { Component, input, output, signal, computed } from '@angular/core';
@Component({
selector: 'app-nome-feature',
standalone: true,
templateUrl: './nome-feature.component.html',
styleUrl: './nome-feature.component.scss',
})
export class NomeFeatureComponent {
// Inputs com input()
titulo = input.required<string>();
dados = input<DadosType[]>([]);
// Outputs com output()
itemClicado = output<DadosType>();
fechar = output<void>();
// Estado com signals
isLoading = signal(false);
searchText = signal('');
// Computed
itensFiltrados = computed(() => {
const termo = this.searchText().toLowerCase();
return this.dados().filter(d => d.nome.toLowerCase().includes(termo));
});
// Métodos
onClicar(item: DadosType): void {
this.itemClicado.emit(item);
}
}
Regras TypeScript
- Standalone: sempre
standalone: true
- Inputs: usar
input() ou input.required()
- Outputs: usar
output()
- Estado: usar
signal()
- Derivados: usar
computed()
- Inject: usar
inject() ao invés de constructor
- Tipagem: sem
any, interfaces com I, enums com E
- Readonly: quando aplicável
- DestroyRef: usar
takeUntilDestroyed() para subscriptions
Nomenclatura TS
- Interface:
IUser, IProduct
- Enum:
EStatus, ERole
- Boolean:
isLoading, hasPermission, canEdit
- Signal:
users (sem $)
- Observable:
users$ (com $)
2. HTML (.component.html)
Regras Obrigatórias
<!-- HTML semântico e acessível -->
<section class="nome-feature">
<header class="nome-feature__header">
<h2>{{ titulo() }}</h2>
</header>
<main class="nome-feature__content">
@if (isLoading()) {
<div class="nome-feature__loading" role="status">
<span>Carregando...</span>
</div>
} @else if (itensFiltrados().length === 0) {
<div class="nome-feature__empty">
<p>Nenhum item encontrado.</p>
</div>
} @else {
<ul class="nome-feature__lista">
@for (item of itensFiltrados(); track item.id) {
<li class="nome-feature__item">
<button
type="button"
(click)="onClicar(item)"
[attr.aria-label]="'Selecionar ' + item.nome"
>
{{ item.nome }}
</button>
</li>
}
</ul>
}
</main>
<footer class="nome-feature__footer">
<button type="button" (click)="fechar.emit()">Fechar</button>
</footer>
</section>
Regras HTML
- Semântico: usar header, main, section, article, footer, nav, button, label
- Acessibilidade: aria-label, role, alt, labels associados
- Semântica: button ao invés de div clicável
- Controle fluxo: @if, @for, @switch (nunca *ngIf, *ngFor)
- Track: sempre em @for com
track item.id
- Sem lógica: não colocar funções complexas no template
- Classes: BEM (bloco__elemento--modificador)
Estrutura CSS Classes (BEM)
.componente
.componente__header
.componente__content
.componente__item
.componente--ativo
.componente--desabilitado
Proibido no HTML
- Div como botão
- Span clicável
- Lógica complexa
- Funções no template
- ngClass/ngStyle (usar @class/@style)
3. SCSS (.component.scss)
Regras Obrigatórias
// 1. Variáveis locais (se necessário)
$componente-padding: 1rem;
// 2. Host
:host {
display: block;
}
// 3. Layout
.nome-feature {
display: flex;
flex-direction: column;
gap: 1rem;
padding: $componente-padding;
// 4. Componentes internos
&__header {
display: flex;
justify-content: space-between;
align-items: center;
}
&__content {
flex: 1;
min-height: 0;
}
&__lista {
display: flex;
flex-direction: column;
gap: 0.5rem;
list-style: none;
padding: 0;
margin: 0;
}
&__item {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
transition: background-color 0.2s ease;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
}
}
// 5. Estados
&__loading,
&__empty {
display: flex;
justify-content: center;
align-items: center;
padding: 2rem;
color: #666;
}
// 6. Responsividade
@media (min-width: 768px) {
padding: 1.5rem;
}
@media (min-width: 1024px) {
padding: 2rem;
}
}
Regras SCSS
- Mobile First: sempre começar do mobile
- Organização: host → layout → componentes → estados → responsivo
- Nesting: máximo 3 níveis
- Seletores: simples, evitar IDs
- Unidades: rem, em, %, clamp() (evitar px)
- Espaçamento: gap ao invés de margin
- Layout: Flexbox/Grid
- Variáveis: CSS Variables ou SCSS vars
- Transições: apenas transform e opacity
Proibido no SCSS
- !important
- Float
- Nesting profundo (>3)
- Seletores específicos
- Inline styles
- @extend complicado
Breakpoints Padrão
// Mobile: padrão (até 767px)
// Tablet: 768px
// Desktop: 1024px
// Wide: 1440px
@media (min-width: 768px) {
/* Tablet */
}
@media (min-width: 1024px) {
/* Desktop */
}
@media (min-width: 1440px) {
/* Wide */
}
Checklist de Validação
TypeScript
HTML
SCSS
Saída Esperada
3 arquivos seguindo padrão do projeto:
nome-feature.component.ts
nome-feature.component.html
nome-feature.component.scss
1---2name: angular-master-component3description: Use quando o usuário pedir para criar componentes Angular completos com HTML, SCSS e TS seguindo o padrão do projeto.4---56# Angular Master Component78## Descrição910Skill completa para criar componentes Angular com HTML, SCSS e TS seguindo o padrão do projeto.1112## Quando Usar1314- Criar novos componentes completos15- Implementar interface de componente do zero16- Quando precisar dos 3 arquivos (TS + HTML + SCSS)1718## Fluxo Obrigatório19201. **Ler regras** (global e local)212. **Ler ANGULAR_CONVENTIONS.MD** (se existir)223. **Analisar padrão** existente do projeto234. **Verificar se já existe** componente similar reutilizável245. **Definir tipo** (Smart/Dumb)256. **Implementar** os 3 arquivos seguindo padrão267. **Ler regras na pasta rules(global e ou local)** e seguir com muita seriedade,2728## Estrutura do Componente2930```31nome-feature/32 nome-feature.component.ts33 nome-feature.component.html34 nome-feature.component.scss35```3637---3839## 1. TypeScript (`.component.ts`)4041### Regras Obrigatórias4243```typescript44import { Component, input, output, signal, computed } from '@angular/core';4546@Component({47 selector: 'app-nome-feature',48 standalone: true,49 templateUrl: './nome-feature.component.html',50 styleUrl: './nome-feature.component.scss',51})52export class NomeFeatureComponent {53 // Inputs com input()54 titulo = input.required<string>();55 dados = input<DadosType[]>([]);5657 // Outputs com output()58 itemClicado = output<DadosType>();59 fechar = output<void>();6061 // Estado com signals62 isLoading = signal(false);63 searchText = signal('');6465 // Computed66 itensFiltrados = computed(() => {67 const termo = this.searchText().toLowerCase();68 return this.dados().filter(d => d.nome.toLowerCase().includes(termo));69 });7071 // Métodos72 onClicar(item: DadosType): void {73 this.itemClicado.emit(item);74 }75}76```7778### Regras TypeScript7980- **Standalone**: sempre `standalone: true`81- **Inputs**: usar `input()` ou `input.required()`82- **Outputs**: usar `output()`83- **Estado**: usar `signal()`84- **Derivados**: usar `computed()`85- **Inject**: usar `inject()` ao invés de constructor86- **Tipagem**: sem `any`, interfaces com `I`, enums com `E`87- **Readonly**: quando aplicável88- **DestroyRef**: usar `takeUntilDestroyed()` para subscriptions8990### Nomenclatura TS9192- Interface: `IUser`, `IProduct`93- Enum: `EStatus`, `ERole`94- Boolean: `isLoading`, `hasPermission`, `canEdit`95- Signal: `users` (sem $)96- Observable: `users$` (com $)9798---99100## 2. HTML (`.component.html`)101102### Regras Obrigatórias103104```html105<!-- HTML semântico e acessível -->106<section class="nome-feature">107 <header class="nome-feature__header">108 <h2>{{ titulo() }}</h2>109 </header>110111 <main class="nome-feature__content">112 @if (isLoading()) {113 <div class="nome-feature__loading" role="status">114 <span>Carregando...</span>115 </div>116 } @else if (itensFiltrados().length === 0) {117 <div class="nome-feature__empty">118 <p>Nenhum item encontrado.</p>119 </div>120 } @else {121 <ul class="nome-feature__lista">122 @for (item of itensFiltrados(); track item.id) {123 <li class="nome-feature__item">124 <button125 type="button"126 (click)="onClicar(item)"127 [attr.aria-label]="'Selecionar ' + item.nome"128 >129 {{ item.nome }}130 </button>131 </li>132 }133 </ul>134 }135 </main>136137 <footer class="nome-feature__footer">138 <button type="button" (click)="fechar.emit()">Fechar</button>139 </footer>140</section>141```142143### Regras HTML144145- **Semântico**: usar header, main, section, article, footer, nav, button, label146- **Acessibilidade**: aria-label, role, alt, labels associados147- **Semântica**: button ao invés de div clicável148- **Controle fluxo**: @if, @for, @switch (nunca *ngIf, *ngFor)149- **Track**: sempre em @for com `track item.id`150- **Sem lógica**: não colocar funções complexas no template151- **Classes**: BEM (bloco\_\_elemento--modificador)152153### Estrutura CSS Classes (BEM)154155```156.componente157.componente__header158.componente__content159.componente__item160.componente--ativo161.componente--desabilitado162```163164### Proibido no HTML165166- Div como botão167- Span clicável168- Lógica complexa169- Funções no template170- ngClass/ngStyle (usar @class/@style)171172---173174## 3. SCSS (`.component.scss`)175176### Regras Obrigatórias177178```scss179// 1. Variáveis locais (se necessário)180$componente-padding: 1rem;181182// 2. Host183:host {184 display: block;185}186187// 3. Layout188.nome-feature {189 display: flex;190 flex-direction: column;191 gap: 1rem;192 padding: $componente-padding;193194 // 4. Componentes internos195 &__header {196 display: flex;197 justify-content: space-between;198 align-items: center;199 }200201 &__content {202 flex: 1;203 min-height: 0;204 }205206 &__lista {207 display: flex;208 flex-direction: column;209 gap: 0.5rem;210 list-style: none;211 padding: 0;212 margin: 0;213 }214215 &__item {216 display: flex;217 align-items: center;218 padding: 0.75rem 1rem;219 border-radius: 0.5rem;220 transition: background-color 0.2s ease;221222 &:hover {223 background-color: rgba(0, 0, 0, 0.05);224 }225 }226227 // 5. Estados228 &__loading,229 &__empty {230 display: flex;231 justify-content: center;232 align-items: center;233 padding: 2rem;234 color: #666;235 }236237 // 6. Responsividade238 @media (min-width: 768px) {239 padding: 1.5rem;240 }241242 @media (min-width: 1024px) {243 padding: 2rem;244 }245}246```247248### Regras SCSS249250- **Mobile First**: sempre começar do mobile251- **Organização**: host → layout → componentes → estados → responsivo252- **Nesting**: máximo 3 níveis253- **Seletores**: simples, evitar IDs254- **Unidades**: rem, em, %, clamp() (evitar px)255- **Espaçamento**: gap ao invés de margin256- **Layout**: Flexbox/Grid257- **Variáveis**: CSS Variables ou SCSS vars258- **Transições**: apenas transform e opacity259260### Proibido no SCSS261262- !important263- Float264- Nesting profundo (>3)265- Seletores específicos266- Inline styles267- @extend complicado268269### Breakpoints Padrão270271```scss272// Mobile: padrão (até 767px)273// Tablet: 768px274// Desktop: 1024px275// Wide: 1440px276277@media (min-width: 768px) {278 /* Tablet */279}280@media (min-width: 1024px) {281 /* Desktop */282}283@media (min-width: 1440px) {284 /* Wide */285}286```287288---289290## Checklist de Validação291292### TypeScript293294- [ ] Standalone: true295- [ ] Inputs com input()/input.required()296- [ ] Outputs com output()297- [ ] Estado com signal()298- [ ] Derivados com computed()299- [ ] Sem any300- [ ] Interfaces com I301- [ ] Enums com E302- [ ] Boolean com is/has/can/should303304### HTML305306- [ ] Semântico (section, header, main, etc)307- [ ] Acessível (aria-label, role)308- [ ] Button ao invés de div309- [ ] @if/@for/@switch310- [ ] Track em @for311- [ ] Sem lógica complexa312- [ ] Classes BEM313314### SCSS315316- [ ] Mobile First317- [ ] Organização correta318- [ ] Nesting máx 3 níveis319- [ ] Sem !important320- [ ] Sem float321- [ ] Responsivo322- [ ] Gap para espaçamento323324---325326## Saída Esperada3273283 arquivos seguindo padrão do projeto:3293301. `nome-feature.component.ts`3312. `nome-feature.component.html`3323. `nome-feature.component.scss`