Files
ui-essentials/COMPONENT_CREATION_TEMPLATE.md
skyai_dev bf67b7f955 Add comprehensive component library expansion with new UI components
This commit includes multiple new components and improvements to the UI essentials library:

## New Components Added:
- **Accordion**: Expandable/collapsible content panels with full accessibility
- **Alert**: Notification and messaging component with variants and dismiss functionality
- **Popover**: Floating content containers with positioning and trigger options
- **Timeline**: Chronological event display with customizable styling
- **Tree View**: Hierarchical data display with expand/collapse functionality
- **Toast**: Notification component (previously committed, includes style refinements)

## Component Features:
- Full TypeScript implementation with Angular 19+ patterns
- Comprehensive SCSS styling using semantic design tokens exclusively
- Multiple size variants (sm, md, lg) and color variants (primary, success, warning, danger, info)
- Accessibility support with ARIA attributes and keyboard navigation
- Responsive design with mobile-first approach
- Interactive demos showcasing all component features
- Integration with existing design system and routing

## Demo Applications:
- Created comprehensive demo components for each new component
- Interactive examples with live code demonstrations
- Integrated into main demo application routing and navigation

## Documentation:
- Added COMPONENT_CREATION_TEMPLATE.md with detailed guidelines
- Comprehensive component creation patterns and best practices
- Design token usage guidelines and validation rules

## Code Quality:
- Follows established Angular and SCSS conventions
- Uses only verified semantic design tokens
- Maintains consistency with existing component architecture
- Comprehensive error handling and edge case management

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 12:02:38 +10:00

884 lines
30 KiB
Markdown

# Component Creation Template
## ⚠️ CRITICAL RESTRICTIONS ⚠️
### DO NOT MODIFY EXISTING COMPONENTS
- **NEVER** alter Angular or SCSS code of already built components
- **NEVER** change existing component files unless explicitly instructed
- **ONLY** create NEW components as specified in the task
- **ONLY** update integration files (routes, menu, public API) to add references to new components
- If asked to modify an existing component, REQUEST CLARIFICATION before proceeding
- Existing components are PRODUCTION CODE and must remain untouched
### Protected Files and Directories
The following should NEVER be modified without explicit permission:
- Any existing component files in `@projects/ui-essentials/src/lib/components/`
- Any existing demo files in `@projects/demo-ui-essentials/src/app/demos/`
- Only ADD new entries to:
- `demos.routes.ts` (add new cases, don't modify existing)
- `dashboard.component.ts` (add new menu items, don't modify existing)
- `public-api.ts` (add new exports, don't modify existing)
---
## Component Creation Task Template
### 1. Component Selection
- Alert/Notification
### 2. Pre-Development Checklist
- [ ] Check if similar component exists in codebase
- [ ] Review existing components in same category for patterns
- [ ] Identify required design tokens from semantic layer
- [ ] Plan component API (inputs, outputs, content projection)
- [ ] **CONFIRM: This is a NEW component, not a modification of an existing one**
### 3. Implementation Steps
#### Step 1: Create Component Structure
**Location:** `@projects/ui-essentials/src/lib/components/[category]/[component-name]/`
Create these NEW files (do not overwrite existing):
- `[component-name].component.ts`
- `[component-name].component.scss`
- `index.ts` (barrel export)
#### Step 2: SCSS Implementation
Follow the **Design Token Guidelines** below to create component styles.
#### Step 3: Angular Component Implementation
Follow the **Angular Component Guidelines** below.
#### Step 4: Create Demo Component
**Location:** `@projects/demo-ui-essentials/src/app/demos/[component-name]-demo/`
- Create NEW demo component showing all variants, sizes, and states
- Include interactive examples and code snippets
- Follow existing demo patterns
#### Step 5: Integration Updates (ADDITIVE ONLY)
##### A. Update Routes (ADD ONLY - DO NOT MODIFY EXISTING)
**File:** `@projects/demo-ui-essentials/src/app/demos/demos.routes.ts`
```typescript
// Add import - DO NOT MODIFY EXISTING IMPORTS
import { [ComponentName]DemoComponent } from './[component-name]-demo/[component-name]-demo.component';
// Add to template switch case - DO NOT MODIFY EXISTING CASES
@case ("[component-name]") {
<ui-[component-name]-demo></ui-[component-name]-demo>
}
// Add to imports array - DO NOT REMOVE OR MODIFY EXISTING ENTRIES
imports: [...existing, [ComponentName]DemoComponent]
```
##### B. Update Dashboard Menu (ADD ONLY - DO NOT MODIFY EXISTING)
**File:** `@projects/demo-ui-essentials/src/app/features/dashboard/dashboard.component.ts`
```typescript
// Import appropriate FontAwesome icon - DO NOT MODIFY EXISTING IMPORTS
import { faIconName } from '@fortawesome/free-solid-svg-icons';
// Add in ngOnInit() - DO NOT MODIFY EXISTING MENU ITEMS
this.addMenuItem("[component-id]", "[Component Label]", this.faIconName);
```
##### C. Update Public API (ADD ONLY - DO NOT MODIFY EXISTING)
**File:** `@projects/ui-essentials/src/public-api.ts`
```typescript
// Add export in appropriate section - DO NOT MODIFY EXISTING EXPORTS
export * from './lib/components/[category]/[component-name]';
```
### ⚠️ INTEGRATION WARNING ⚠️
When updating integration files:
- **ONLY ADD** new entries
- **NEVER DELETE** existing entries
- **NEVER MODIFY** existing entries
- **PRESERVE** all existing formatting and structure
- **TEST** that existing components still work after changes
---
## Design Token Guidelines ⚠️ CRITICAL: ONLY USE EXISTING TOKENS ⚠️
### MANDATORY: Import and Use Semantic Tokens
```scss
@use '@projects/shared-ui/src/styles/semantic' as *;
```
### 🚨 TOKEN VALIDATION RULES 🚨
-**NEVER** invent or guess token names
-**NEVER** use tokens not listed below
-**NEVER** hardcode values (px, rem, #hex colors, etc.)
-**ALWAYS** use semantic tokens for ALL values
-**ALWAYS** check this reference before using any token
-**ASK FOR GUIDANCE** if needed token doesn't exist
### Available Token Categories (VERIFIED ACTUAL TOKENS):
## 1. **COLORS** (Single Values - Use Directly)
```scss
// Text Colors
$semantic-color-text-primary // Primary text
$semantic-color-text-secondary // Secondary text
$semantic-color-text-tertiary // Tertiary text
$semantic-color-text-disabled // Disabled text
$semantic-color-text-inverse // Inverse text
// Surface Colors
$semantic-color-surface-primary // Primary surface
$semantic-color-surface-secondary // Secondary surface
$semantic-color-surface-elevated // Elevated surface
$semantic-color-surface // Base surface
$semantic-color-surface-container // Container surface
// Border Colors
$semantic-color-border-primary // Primary border
$semantic-color-border-secondary // Secondary border
$semantic-color-border-subtle // Subtle border
$semantic-color-border-focus // Focus border
$semantic-color-border-error // Error border
// Brand Colors
$semantic-color-primary // Primary brand
$semantic-color-secondary // Secondary brand
$semantic-color-on-primary // Text on primary
$semantic-color-on-secondary // Text on secondary
// Feedback Colors
$semantic-color-success // Success state
$semantic-color-warning // Warning state
$semantic-color-danger // Danger/error state
$semantic-color-info // Info state
$semantic-color-on-success // Text on success
$semantic-color-on-warning // Text on warning
$semantic-color-on-danger // Text on danger
$semantic-color-on-info // Text on info
// Focus and Interactive
$semantic-color-focus // Focus indicator
$semantic-color-interactive-primary // Interactive primary
$semantic-color-backdrop // Backdrop/overlay
```
## 2. **TYPOGRAPHY** (Maps and Single Values)
### Typography Maps (Use with map-get())
```scss
// Headings (Maps - Extract individual properties)
$semantic-typography-heading-h1 // h1 heading map
$semantic-typography-heading-h2 // h2 heading map
$semantic-typography-heading-h3 // h3 heading map
$semantic-typography-heading-h4 // h4 heading map
$semantic-typography-heading-h5 // h5 heading map
// Body Text (Maps)
$semantic-typography-body-large // Large body text map
$semantic-typography-body-medium // Medium body text map
$semantic-typography-body-small // Small body text map
// UI Components (Maps)
$semantic-typography-button-large // Large button text map
$semantic-typography-button-medium // Medium button text map
$semantic-typography-button-small // Small button text map
$semantic-typography-label // Form label map
$semantic-typography-input // Input text map
$semantic-typography-caption // Caption text map
// Usage Example for Maps:
font-family: map-get($semantic-typography-body-medium, font-family);
font-size: map-get($semantic-typography-body-medium, font-size);
font-weight: map-get($semantic-typography-body-medium, font-weight);
line-height: map-get($semantic-typography-body-medium, line-height);
```
### Typography Single Values (Use Directly)
```scss
// Font Weights
$semantic-typography-font-weight-normal
$semantic-typography-font-weight-medium
$semantic-typography-font-weight-semibold
$semantic-typography-font-weight-bold
// Font Sizes
$semantic-typography-font-size-xs
$semantic-typography-font-size-sm
$semantic-typography-font-size-md
$semantic-typography-font-size-lg
$semantic-typography-font-size-xl
$semantic-typography-font-size-2xl
$semantic-typography-font-size-3xl
// Line Heights
$semantic-typography-line-height-tight
$semantic-typography-line-height-normal
$semantic-typography-line-height-relaxed
// Font Families
$semantic-typography-font-family-sans
$semantic-typography-font-family-serif
$semantic-typography-font-family-mono
```
## 3. **SPACING** (Single Values - Use Directly)
```scss
// Component Spacing
$semantic-spacing-component-xs // Extra small component spacing
$semantic-spacing-component-sm // Small component spacing
$semantic-spacing-component-md // Medium component spacing
$semantic-spacing-component-lg // Large component spacing
$semantic-spacing-component-xl // Extra large component spacing
// Layout Spacing
$semantic-spacing-layout-section-xs // Extra small section spacing
$semantic-spacing-layout-section-sm // Small section spacing
$semantic-spacing-layout-section-md // Medium section spacing
$semantic-spacing-layout-section-lg // Large section spacing
$semantic-spacing-layout-section-xl // Extra large section spacing
// Content Spacing
$semantic-spacing-content-paragraph // Paragraph spacing
$semantic-spacing-content-heading // Heading spacing
$semantic-spacing-content-list-item // List item spacing
$semantic-spacing-content-line-tight // Tight line spacing
// Interactive Spacing
$semantic-spacing-interactive-button-padding-x // Button horizontal padding
$semantic-spacing-interactive-button-padding-y // Button vertical padding
$semantic-spacing-interactive-input-padding-x // Input horizontal padding
$semantic-spacing-interactive-input-padding-y // Input vertical padding
// Grid and Stack Spacing
$semantic-spacing-grid-gap-sm // Small grid gap
$semantic-spacing-grid-gap-md // Medium grid gap
$semantic-spacing-grid-gap-lg // Large grid gap
$semantic-spacing-stack-sm // Small stack spacing
$semantic-spacing-stack-md // Medium stack spacing
$semantic-spacing-stack-lg // Large stack spacing
// Form Spacing
$semantic-spacing-form-field-gap // Form field gap
$semantic-spacing-form-group-gap // Form group gap
```
## 4. **BORDERS** (Single Values - Use Directly)
```scss
// Border Widths
$semantic-border-width-1 // 1px border
$semantic-border-width-2 // 2px border
$semantic-border-card-width // Standard card border width
// Border Radius
$semantic-border-radius-sm // Small radius
$semantic-border-radius-md // Medium radius
$semantic-border-radius-lg // Large radius
$semantic-border-radius-xl // Extra large radius
$semantic-border-radius-full // Full/circle radius
// Component Borders
$semantic-border-button-radius // Button border radius
$semantic-border-input-radius // Input border radius
$semantic-border-card-radius // Card border radius
```
## 5. **SHADOWS** (Single Values - Use Directly)
```scss
// Elevation Shadows
$semantic-shadow-elevation-0 // No shadow
$semantic-shadow-elevation-1 // Level 1 elevation
$semantic-shadow-elevation-2 // Level 2 elevation
$semantic-shadow-elevation-3 // Level 3 elevation
$semantic-shadow-elevation-4 // Level 4 elevation
$semantic-shadow-elevation-5 // Level 5 elevation
// Component Shadows
$semantic-shadow-card-rest // Card default shadow
$semantic-shadow-card-hover // Card hover shadow
$semantic-shadow-button-rest // Button default shadow
$semantic-shadow-button-hover // Button hover shadow
$semantic-shadow-dropdown // Dropdown shadow
$semantic-shadow-modal // Modal shadow
```
## 6. **MOTION** (Single Values and Maps)
### Motion Single Values (Use Directly)
```scss
// Duration
$semantic-motion-duration-fast // Fast duration
$semantic-motion-duration-normal // Normal duration
$semantic-motion-duration-slow // Slow duration
// Easing
$semantic-motion-easing-ease // Standard easing
$semantic-motion-easing-ease-in-out // Ease in-out
$semantic-motion-easing-spring // Spring easing
```
## 7. **SIZING** (Single Values - Use Directly)
```scss
// Button Heights
$semantic-sizing-button-height-sm // Small button height
$semantic-sizing-button-height-md // Medium button height
$semantic-sizing-button-height-lg // Large button height
// Input Heights
$semantic-sizing-input-height-sm // Small input height
$semantic-sizing-input-height-md // Medium input height
$semantic-sizing-input-height-lg // Large input height
// Icon Sizes
$semantic-sizing-icon-inline // Inline icon size
$semantic-sizing-icon-button // Button icon size
$semantic-sizing-icon-navigation // Navigation icon size
// Touch Targets
$semantic-sizing-touch-minimum // Minimum touch target
$semantic-sizing-touch-target // Standard touch target
```
## 8. **Z-INDEX** (Single Values - Use Directly)
```scss
$semantic-z-index-dropdown // Dropdown layer
$semantic-z-index-modal // Modal layer
$semantic-z-index-tooltip // Tooltip layer
$semantic-z-index-overlay // Overlay layer
```
## 9. **OPACITY** (Single Values - Use Directly)
```scss
$semantic-opacity-disabled // Disabled opacity
$semantic-opacity-hover // Hover opacity
$semantic-opacity-backdrop // Backdrop opacity
$semantic-opacity-subtle // Subtle opacity
```
### Strict Rules:
-**NEVER** hardcode values (px, rem, #hex colors, etc.)
-**NEVER** use base tokens directly (from `/base/` directory)
-**NEVER** use magic numbers
-**ALWAYS** use semantic tokens for ALL values
-**ALWAYS** follow BEM naming convention for classes
-**ALWAYS** use SCSS nesting appropriately (max 3 levels)
-**ALWAYS** include responsive breakpoints using `$semantic-breakpoint-*` tokens
-**ALWAYS** maintain consistency with existing components
### Component SCSS Template (CORRECTED)
```scss
@use '@projects/shared-ui/src/styles/semantic' as *;
.ui-[component-name] {
// Core Structure
display: flex;
position: relative;
// Layout & Spacing - ONLY USE ACTUAL TOKENS
padding: $semantic-spacing-component-md;
margin: $semantic-spacing-layout-section-sm;
// Visual Design - ONLY USE ACTUAL TOKENS
background: $semantic-color-surface-primary;
border: $semantic-border-card-width solid $semantic-color-border-primary;
border-radius: $semantic-border-card-radius;
box-shadow: $semantic-shadow-elevation-1;
// Typography - USE MAP-GET FOR MAPS, SINGLE VALUES DIRECTLY
font-family: map-get($semantic-typography-body-medium, font-family);
font-size: map-get($semantic-typography-body-medium, font-size);
font-weight: map-get($semantic-typography-body-medium, font-weight);
line-height: map-get($semantic-typography-body-medium, line-height);
color: $semantic-color-text-primary;
// Transitions - ONLY USE ACTUAL TOKENS
transition: all $semantic-motion-duration-fast $semantic-motion-easing-ease;
// Sizing Variants - ONLY USE ACTUAL TOKENS
&--sm {
min-height: $semantic-sizing-button-height-sm;
padding: $semantic-spacing-component-xs;
font-family: map-get($semantic-typography-body-small, font-family);
font-size: map-get($semantic-typography-body-small, font-size);
font-weight: map-get($semantic-typography-body-small, font-weight);
line-height: map-get($semantic-typography-body-small, line-height);
}
&--md {
min-height: $semantic-sizing-button-height-md;
padding: $semantic-spacing-component-sm;
// Typography already set above for medium
}
&--lg {
min-height: $semantic-sizing-button-height-lg;
padding: $semantic-spacing-component-lg;
font-family: map-get($semantic-typography-body-large, font-family);
font-size: map-get($semantic-typography-body-large, font-size);
font-weight: map-get($semantic-typography-body-large, font-weight);
line-height: map-get($semantic-typography-body-large, line-height);
}
// Color Variants - ONLY USE ACTUAL TOKENS
&--primary {
background: $semantic-color-primary;
color: $semantic-color-on-primary;
border-color: $semantic-color-primary;
}
&--secondary {
background: $semantic-color-secondary;
color: $semantic-color-on-secondary;
border-color: $semantic-color-secondary;
}
&--success {
background: $semantic-color-success;
color: $semantic-color-on-success;
border-color: $semantic-color-success;
}
&--danger {
background: $semantic-color-danger;
color: $semantic-color-on-danger;
border-color: $semantic-color-danger;
}
// State Variants - ONLY USE ACTUAL TOKENS
&--disabled {
opacity: $semantic-opacity-disabled;
cursor: not-allowed;
pointer-events: none;
}
// BEM Element - ONLY USE ACTUAL TOKENS
&__element {
padding: $semantic-spacing-content-line-tight;
color: $semantic-color-text-secondary;
// Element modifier
&--active {
color: $semantic-color-text-primary;
font-weight: $semantic-typography-font-weight-semibold;
}
}
// Interactive States - ONLY USE ACTUAL TOKENS
&:not(.ui-[component-name]--disabled) {
&:hover {
box-shadow: $semantic-shadow-card-hover;
}
&:focus-visible {
outline: 2px solid $semantic-color-focus;
outline-offset: 2px;
}
&:active {
box-shadow: $semantic-shadow-card-rest;
}
}
// Responsive Design - USE ACTUAL BREAKPOINT TOKENS
@media (max-width: $semantic-breakpoint-md - 1) {
padding: $semantic-spacing-component-sm;
}
@media (max-width: $semantic-breakpoint-sm - 1) {
padding: $semantic-spacing-component-xs;
font-family: map-get($semantic-typography-body-small, font-family);
font-size: map-get($semantic-typography-body-small, font-size);
font-weight: map-get($semantic-typography-body-small, font-weight);
line-height: map-get($semantic-typography-body-small, line-height);
}
}
```
### 🚨 CRITICAL TOKEN VALIDATION 🚨
#### ❌ COMMON MISTAKES TO AVOID:
```scss
// WRONG - These tokens DON'T EXIST:
font-size: $semantic-typography-heading-h3; // This is a MAP, not a single value!
padding: $semantic-spacing-layout-xs; // DOESN'T EXIST
box-shadow: $semantic-shadow-card-featured; // DOESN'T EXIST
border-radius: $semantic-border-radius-xs; // DOESN'T EXIST
transition: $semantic-motion-duration-medium; // DOESN'T EXIST
background: $semantic-color-background-primary; // DOESN'T EXIST
// WRONG - Hardcoded values:
padding: 16px; // Use $semantic-spacing-component-md
color: #333333; // Use $semantic-color-text-primary
border-radius: 8px; // Use $semantic-border-radius-md
```
#### ✅ CORRECT USAGE:
```scss
// RIGHT - Use map-get for typography maps:
font-size: map-get($semantic-typography-heading-h3, font-size);
font-weight: map-get($semantic-typography-heading-h3, font-weight);
// RIGHT - Use single value tokens directly:
padding: $semantic-spacing-component-md;
color: $semantic-color-text-primary;
border-radius: $semantic-border-card-radius;
box-shadow: $semantic-shadow-elevation-2;
```
### ⚠️ MAP USAGE WARNING ⚠️
These tokens are **MAPS** and require `map-get()`:
- `$semantic-typography-heading-*` (h1, h2, h3, h4, h5)
- `$semantic-typography-body-*` (large, medium, small)
- `$semantic-typography-button-*` (large, medium, small)
- `$semantic-typography-label`
- `$semantic-typography-input`
- `$semantic-typography-caption`
**Always extract individual properties:**
```scss
// CORRECT MAP USAGE:
font-family: map-get($semantic-typography-body-medium, font-family);
font-size: map-get($semantic-typography-body-medium, font-size);
font-weight: map-get($semantic-typography-body-medium, font-weight);
line-height: map-get($semantic-typography-body-medium, line-height);
```
### Expected Deliverables:
1. **SCSS file** properly structured with ONLY existing semantic tokens
2. **BEM methodology** consistently applied throughout
3. **Responsive design** using actual breakpoint tokens
4. **Interactive states** (hover, focus, active) defined with real tokens
5. **Zero hardcoded values** - everything uses verified design tokens
6. **Proper map usage** for typography tokens
7. **Comments** explaining major sections if complex
### Quality Checklist:
- [ ] All colors use actual `$semantic-color-*` tokens
- [ ] All spacing uses actual `$semantic-spacing-*` tokens
- [ ] All typography uses proper map-get() for maps or single tokens
- [ ] All shadows use actual `$semantic-shadow-*` tokens
- [ ] All borders use actual `$semantic-border-*` tokens
- [ ] All motion uses actual `$semantic-motion-*` tokens
- [ ] **No hardcoded values anywhere**
- [ ] **No invented token names**
- [ ] BEM naming convention followed
- [ ] Responsive breakpoints implemented with actual tokens
- [ ] File saved in correct directory
---
## Angular Component Guidelines (Enhanced)
### Angular 19+ Development Standards:
#### TEMPLATES:
- Use inline templates in @Component decorator
- Use new control flow syntax: @if, @for, @switch (NOT *ngIf, *ngFor)
- Use @defer for lazy loading where appropriate
- Put stylesheets in a separate scss file to the component
#### STYLING:
- Import design tokens: @use '@projects/shared-ui/src/styles/semantic' as *;
- **CRITICAL:** Ensure correct semantic tokens are used. Do not invent new ones.
- **If a semantic variable is required but doesn't exist, ASK FOR INSTRUCTION**
- NO hardcoded values - use only design tokens for colors, spacing, typography, breakpoints
- Follow BEM naming convention for class names
- Use SCSS nesting judiciously (max 3 levels)
- Prefer mixins and functions from the design system
#### TYPESCRIPT:
- Use standalone components (no NgModules)
- Prefer signals over observables for state management
- Use inject() function over constructor injection
- Strong typing everywhere - avoid 'any'
- Use readonly where applicable
- Implement OnPush change detection by default
#### STRUCTURE:
- Feature-based folder structure
- Barrel exports for clean imports
- Smart/presentational component pattern
- Services should be provided in root unless scoped needed
### Component Template
```typescript
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
type ComponentSize = 'sm' | 'md' | 'lg';
type ComponentVariant = 'primary' | 'secondary' | 'success' | 'danger';
@Component({
selector: 'ui-[component-name]',
standalone: true,
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
template: `
<div
class="ui-[component-name]"
[class.ui-[component-name]--{{size}}]="size"
[class.ui-[component-name]--{{variant}}]="variant"
[class.ui-[component-name]--disabled]="disabled"
[class.ui-[component-name]--loading]="loading"
[attr.aria-disabled]="disabled"
[attr.aria-busy]="loading"
[attr.role]="role"
[tabindex]="disabled ? -1 : tabIndex"
(click)="handleClick($event)"
(keydown)="handleKeydown($event)">
@if (loading) {
<span class="ui-[component-name]__loader" aria-hidden="true"></span>
}
<ng-content></ng-content>
</div>
`,
styleUrl: './[component-name].component.scss'
})
export class [ComponentName]Component {
@Input() size: ComponentSize = 'md';
@Input() variant: ComponentVariant = 'primary';
@Input() disabled = false;
@Input() loading = false;
@Input() role = '[appropriate-aria-role]';
@Input() tabIndex = 0;
@Output() clicked = new EventEmitter<MouseEvent>();
handleClick(event: MouseEvent): void {
if (!this.disabled && !this.loading) {
this.clicked.emit(event);
}
}
handleKeydown(event: KeyboardEvent): void {
// Implement keyboard navigation
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.handleClick(event as any);
}
}
}
```
### Demo Component Template
```typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { [ComponentName]Component } from '../[component-name].component';
@Component({
selector: 'ui-[component-name]-demo',
standalone: true,
imports: [CommonModule, [ComponentName]Component],
template: `
<div class="demo-container">
<h2>[Component Name] Demo</h2>
<!-- Size Variants -->
<section class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
@for (size of sizes; track size) {
<ui-[component-name] [size]="size">
{{ size }} size
</ui-[component-name]>
}
</div>
</section>
<!-- Color Variants -->
<section class="demo-section">
<h3>Variants</h3>
<div class="demo-row">
@for (variant of variants; track variant) {
<ui-[component-name] [variant]="variant">
{{ variant }}
</ui-[component-name]>
}
</div>
</section>
<!-- States -->
<section class="demo-section">
<h3>States</h3>
<div class="demo-row">
<ui-[component-name] [disabled]="true">Disabled</ui-[component-name]>
<ui-[component-name] [loading]="true">Loading</ui-[component-name]>
</div>
</section>
<!-- Interactive Example -->
<section class="demo-section">
<h3>Interactive</h3>
<ui-[component-name] (clicked)="handleDemoClick($event)">
Click me
</ui-[component-name]>
<p>Clicked: {{ clickCount }} times</p>
</section>
</div>
`,
styleUrl: './[component-name]-demo.component.scss'
})
export class [ComponentName]DemoComponent {
sizes = ['sm', 'md', 'lg'] as const;
variants = ['primary', 'secondary', 'success', 'danger'] as const;
clickCount = 0;
handleDemoClick(event: MouseEvent): void {
this.clickCount++;
console.log('Component clicked', event);
}
}
```
### Component Requirements
- **Design Tokens**: Use ONLY verified semantic tokens throughout
- **Variants**: Include common UI system variants (colors, styles)
- **Sizes**: sm, md, lg sizing options
- **States**: hover, focus, active, disabled
- **Accessibility**: ARIA support, keyboard navigation
---
## Testing Requirements
### Unit Test Template
```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { [ComponentName]Component } from './[component-name].component';
describe('[ComponentName]Component', () => {
let component: [ComponentName]Component;
let fixture: ComponentFixture<[ComponentName]Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [[ComponentName]Component]
}).compileComponents();
fixture = TestBed.createComponent([ComponentName]Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should apply size classes correctly', () => {
component.size = 'lg';
fixture.detectChanges();
const element = fixture.nativeElement.querySelector('.ui-[component-name]');
expect(element).toHaveClass('ui-[component-name]--lg');
});
it('should handle disabled state', () => {
component.disabled = true;
fixture.detectChanges();
const element = fixture.nativeElement.querySelector('.ui-[component-name]');
expect(element).toHaveClass('ui-[component-name]--disabled');
expect(element.getAttribute('aria-disabled')).toBe('true');
});
it('should emit click events when not disabled', () => {
spyOn(component.clicked, 'emit');
const element = fixture.nativeElement.querySelector('.ui-[component-name]');
element.click();
expect(component.clicked.emit).toHaveBeenCalled();
});
it('should not emit click events when disabled', () => {
component.disabled = true;
spyOn(component.clicked, 'emit');
const element = fixture.nativeElement.querySelector('.ui-[component-name]');
element.click();
expect(component.clicked.emit).not.toHaveBeenCalled();
});
it('should support keyboard navigation', () => {
spyOn(component, 'handleClick');
const element = fixture.nativeElement.querySelector('.ui-[component-name]');
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
element.dispatchEvent(enterEvent);
expect(component.handleClick).toHaveBeenCalled();
});
});
```
---
## Component Quality Checklist
### ⚠️ PRE-FLIGHT CHECK ⚠️
- [ ] **CONFIRMED: Creating NEW component, not modifying existing**
- [ ] **VERIFIED: No existing component files will be altered**
- [ ] **CHECKED: Only adding to integration files, not modifying**
### Functionality
- [ ] All size variants work correctly (sm, md, lg)
- [ ] All color variants apply properly
- [ ] Disabled state prevents interaction
- [ ] Loading state shows indicator
- [ ] Events emit correctly
- [ ] Content projection works
### Styling ⚠️ CRITICAL VALIDATION ⚠️
- [ ] **Uses ONLY verified existing semantic tokens**
- [ ] **NO HARDCODED VALUES** (px, rem, hex colors)
- [ ] **NO INVENTED TOKEN NAMES**
- [ ] Responsive on all breakpoints
- [ ] Follows BEM naming convention
- [ ] Proper state transitions
- [ ] Map tokens used with map-get()
### Accessibility
- [ ] Proper ARIA attributes
- [ ] Keyboard navigable
- [ ] Focus indicators visible
- [ ] Screen reader friendly
- [ ] Meets WCAG 2.1 AA standards
- [ ] Color contrast sufficient
### Code Quality
- [ ] TypeScript strictly typed
- [ ] OnPush change detection
- [ ] Standalone component
- [ ] Proper imports/exports
- [ ] Demo shows all features
- [ ] Unit tests pass
### Integration
- [ ] Added to public API (additive only)
- [ ] Demo route configured (additive only)
- [ ] Menu item added to dashboard (additive only)
- [ ] Build passes without errors
- [ ] Follows project conventions
- [ ] **Existing components still work**
---
## 🚨 FINAL TOKEN VALIDATION 🚨
### Before Implementation:
1. **VERIFY** every token used exists in the reference above
2. **ASK FOR HELP** if you need a token that doesn't exist
3. **TEST** build process to catch any invalid tokens
4. **NEVER GUESS** or invent token names
### If you need a token that doesn't exist:
- **STOP** implementation
- **ASK** "This component needs [specific token type] but I don't see one available. Should I [alternative approach] or do you have guidance?"
- **WAIT** for direction rather than inventing tokens
This template provides a comprehensive guide for creating consistent, high-quality NEW components that integrate seamlessly with the existing design system without affecting any existing code, **and most importantly, without using non-existent tokens that would cause build failures**.