AI Skill Report Card

Building Vanilla TypeScript UI Components

B+78·Jun 13, 2026·Source: Web
15 / 15
TypeScript
// components/Button/Button.ts export interface ButtonProps { text: string; variant?: 'primary' | 'secondary'; onClick?: () => void; } export class Button { private element: HTMLButtonElement; constructor(private props: ButtonProps) { this.element = this.createElement(); this.bindEvents(); } private createElement(): HTMLButtonElement { const button = document.createElement('button'); button.textContent = this.props.text; button.className = `btn btn--${this.props.variant || 'primary'}`; return button; } private bindEvents(): void { if (this.props.onClick) { this.element.addEventListener('click', this.props.onClick); } } public render(): HTMLButtonElement { return this.element; } public destroy(): void { this.element.removeEventListener('click', this.props.onClick!); } }
Recommendation
The workflow checklist could be more specific about testing and validation steps rather than just implementation steps
12 / 15

Progress:

  • Define component interface and props
  • Create component class with constructor
  • Implement createElement() method
  • Add event binding in bindEvents()
  • Implement render() method
  • Add destroy() method for cleanup
  • Create accompanying CSS file
  • Add barrel export in index.ts
  • Write usage example

Folder Structure

src/
├── components/
│   ├── Button/
│   │   ├── Button.ts
│   │   ├── Button.css
│   │   └── index.ts
│   └── index.ts
├── types/
│   └── common.ts
├── utils/
│   └── dom.ts
└── styles/
    └── variables.css
Recommendation
Add concrete pitfall examples showing 'bad vs good' code patterns rather than just listing warnings
18 / 20

Example 1: Input Component Input:

TypeScript
interface InputProps { placeholder?: string; type?: 'text' | 'email' | 'password'; onInput?: (value: string) => void; }

Output:

TypeScript
export class Input { private element: HTMLInputElement; constructor(private props: InputProps) { this.element = this.createElement(); this.bindEvents(); } private createElement(): HTMLInputElement { const input = document.createElement('input'); input.type = this.props.type || 'text'; input.placeholder = this.props.placeholder || ''; input.className = 'input'; return input; } private bindEvents(): void { if (this.props.onInput) { this.element.addEventListener('input', (e) => { this.props.onInput!((e.target as HTMLInputElement).value); }); } } public render(): HTMLInputElement { return this.element; } public getValue(): string { return this.element.value; } public destroy(): void { this.element.removeEventListener('input', this.props.onInput!); } }

Example 2: Component Usage Input: Using components in application Output:

TypeScript
// App.ts import { Button } from './components/Button'; import { Input } from './components/Input'; class App { private container: HTMLElement; constructor(containerId: string) { this.container = document.getElementById(containerId)!; this.render(); } private render(): void { const input = new Input({ placeholder: 'Enter your name', onInput: (value) => console.log(value) }); const button = new Button({ text: 'Submit', variant: 'primary', onClick: () => console.log('Clicked!') }); this.container.append(input.render(), button.render()); } } new App('app');
Recommendation
Consider adding a utilities section with common DOM helper functions that components typically need
  • Use TypeScript interfaces for all component props
  • Implement proper cleanup in destroy() methods to prevent memory leaks
  • Keep components stateless when possible - pass data through props
  • Use CSS custom properties for theming
  • Create barrel exports (index.ts) for clean imports
  • Separate concerns: one class per component, styles in separate CSS files
  • Use semantic HTML elements as base (button, input, nav, etc.)
  • Implement consistent naming: PascalCase for classes, camelCase for methods
  • Add type guards for DOM element validation
  • Forgetting to remove event listeners in destroy() method
  • Not validating DOM element existence before manipulation
  • Mixing presentation logic with business logic in components
  • Creating components that are too tightly coupled to specific data structures
  • Not handling edge cases (null/undefined props)
  • Overusing innerHTML instead of creating elements programmatically
  • Not implementing proper TypeScript strict mode compliance
  • Creating components without considering reusability
  • Forgetting to export components from barrel files
0
Grade B+AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
15/15
Workflow
12/15
Examples
18/20
Completeness
16/20
Format
15/15
Conciseness
12/15