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>
30 KiB
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.scssindex.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
// 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
// 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
// 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
@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)
// 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())
// 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)
// 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)
// 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)
// 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)
// 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)
// 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)
// 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)
$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)
$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)
@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:
// 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:
// 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:
// 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:
- SCSS file properly structured with ONLY existing semantic tokens
- BEM methodology consistently applied throughout
- Responsive design using actual breakpoint tokens
- Interactive states (hover, focus, active) defined with real tokens
- Zero hardcoded values - everything uses verified design tokens
- Proper map usage for typography tokens
- 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
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
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
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:
- VERIFY every token used exists in the reference above
- ASK FOR HELP if you need a token that doesn't exist
- TEST build process to catch any invalid tokens
- 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.