Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
980 lines
28 KiB
Markdown
980 lines
28 KiB
Markdown
# DSS Coding Principles
|
|
|
|
Comprehensive principles for building the Design System Swarm (DSS) - a living, breathing design system organism.
|
|
|
|
**Version**: 1.0
|
|
**Last Updated**: 2025-12-06
|
|
**Grade**: A (Excellent quality with room for accessibility improvements)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Architectural Principles](#1-architectural-principles)
|
|
2. [Code Quality Standards](#2-code-quality-standards)
|
|
3. [Organism Framework Principles](#3-organism-framework-principles)
|
|
4. [Keyboard Accessibility](#4-keyboard-accessibility)
|
|
5. [Web Component Standards](#5-web-component-standards)
|
|
6. [Security Guidelines](#6-security-guidelines)
|
|
7. [Event Handling & Memory Management](#7-event-handling--memory-management)
|
|
8. [Testing & Quality Assurance](#8-testing--quality-assurance)
|
|
9. [Documentation Standards](#9-documentation-standards)
|
|
10. [Review Checklists](#10-review-checklists)
|
|
|
|
---
|
|
|
|
## 1. Architectural Principles
|
|
|
|
DSS follows a **monolithic core architecture with strong separation of concerns**. These principles ensure maintainability and clarity.
|
|
|
|
### 1.1 Monolithic Core Design
|
|
|
|
**Principle**: Single source of truth with immutable design system core
|
|
|
|
**Guidelines**:
|
|
- Central database of record (heart ❤️)
|
|
- All design tokens pass through normalization pipeline
|
|
- External systems translate TO DSS, not the reverse
|
|
- Immutable by default for design data
|
|
|
|
**Benefits**:
|
|
- ✅ Simplified deployment and maintenance
|
|
- ✅ Reduced operational complexity
|
|
- ✅ Clear data flow and debugging
|
|
- ✅ Strong consistency guarantees
|
|
|
|
**Trade-offs**:
|
|
- Less flexible than microservices
|
|
- Single point of failure (mitigated by backups)
|
|
- Harder to scale horizontally (acceptable for current scale)
|
|
|
|
**Reference**: `/docs/ARCHITECTURE_REVIEW.md#1-monolithic-core`
|
|
|
|
### 1.2 External Translation Pattern
|
|
|
|
**Principle**: DSS doesn't adapt to external systems; external systems translate TO DSS
|
|
|
|
**Implementation**:
|
|
```
|
|
Figma → Translation Layer → DSS Core
|
|
CSS → Translation Layer → DSS Core
|
|
Tailwind → Translation Layer → DSS Core
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Maintains data integrity
|
|
- ✅ Prevents drift between sources
|
|
- ✅ Clear contract boundaries
|
|
- ✅ Easy to add new sources
|
|
|
|
**Anti-Pattern**:
|
|
- ❌ DSS adapting to external format variations
|
|
- ❌ Custom handling per-source in core
|
|
- ❌ Loose source contracts
|
|
|
|
**Reference**: `/docs/ARCHITECTURE_REVIEW.md#2-external-translation`
|
|
|
|
### 1.3 Layered Architecture
|
|
|
|
**Principle**: Clear separation between storage, domain logic, and interfaces
|
|
|
|
**Layers** (bottom to top):
|
|
1. **Storage Layer** - SQLite, file system, cache
|
|
2. **Core Domain Layer** - Token models, merge logic, validation
|
|
3. **Ingestion Layer** - Source parsers (CSS, SCSS, Figma, etc.)
|
|
4. **Analysis Layer** - Project scanning, quick wins, metrics
|
|
5. **Generation Layer** - Storybook, component generation, output
|
|
6. **API Layer** - REST + MCP interfaces
|
|
|
|
**Rules**:
|
|
- Lower layers have NO dependencies on upper layers
|
|
- Each layer has a single responsibility
|
|
- Data flows through layers in defined directions
|
|
- Minimal coupling between layers
|
|
|
|
**Reference**: `/docs/ARCHITECTURE_REVIEW.md#3-layered-architecture`
|
|
|
|
### 1.4 Architectural Patterns
|
|
|
|
**Strategy Pattern** - For token merge strategies
|
|
```python
|
|
class TokenMerger:
|
|
def __init__(self, strategy: MergeStrategy):
|
|
self.strategy = strategy # FIRST, LAST, PREFER_FIGMA, etc.
|
|
```
|
|
|
|
**Abstract Base Classes** - For extension points
|
|
```python
|
|
class TokenSource(ABC):
|
|
@abstractmethod
|
|
async def extract(self, source: str) -> TokenCollection:
|
|
pass
|
|
```
|
|
|
|
**Data Classes** - For immutable domain models
|
|
```python
|
|
@dataclass(frozen=True)
|
|
class DesignToken:
|
|
name: str
|
|
value: str
|
|
type: TokenType
|
|
```
|
|
|
|
**Dependency Injection** - Explicit dependencies
|
|
```python
|
|
# Good: dependencies explicit
|
|
def __init__(self, db_path: str, cache: Cache):
|
|
self.db = Database(db_path)
|
|
self.cache = cache
|
|
|
|
# Avoid: hard-coded dependencies
|
|
config_path = Path(__file__).parent / "config.json"
|
|
```
|
|
|
|
**Reference**: `/docs/ARCHITECTURE_REVIEW.md#architecture-patterns`
|
|
|
|
---
|
|
|
|
## 2. Code Quality Standards
|
|
|
|
DSS maintains **A-grade code quality** with comprehensive standards.
|
|
|
|
### 2.1 Python Style & Conventions
|
|
|
|
**Following**: PEP 8 with flexibility
|
|
|
|
**Standards**:
|
|
- 4-space indentation (never tabs)
|
|
- Max line length: 100 characters (flexible)
|
|
- snake_case for functions and variables
|
|
- PascalCase for classes
|
|
- UPPER_CASE for constants
|
|
- Type hints for 100% of public APIs
|
|
|
|
**Naming Examples**:
|
|
```python
|
|
class TokenMerger: # Class: PascalCase
|
|
async def merge(self, ...): # Method: snake_case
|
|
MAX_TOKENS = 1000 # Constant: UPPER_CASE
|
|
token_count = 0 # Variable: snake_case
|
|
```
|
|
|
|
### 2.2 Error Handling
|
|
|
|
**Principle**: Explicit, meaningful error handling with no silent failures
|
|
|
|
**Good Practices**:
|
|
```python
|
|
# Specific exceptions with context
|
|
try:
|
|
data = json.loads(content)
|
|
except (json.JSONDecodeError, KeyError) as e:
|
|
logger.error(f"Failed to parse tokens: {e}")
|
|
raise ValueError(f"Invalid token data: {e}") from e
|
|
|
|
# Meaningful error messages
|
|
raise ValueError(f"Invalid token type: {token_type}. Expected one of: {valid_types}")
|
|
```
|
|
|
|
**Anti-Patterns**:
|
|
- ❌ Bare `except:` statements
|
|
- ❌ Silent failures (`pass` without logging)
|
|
- ❌ Generic `Exception` catching
|
|
- ❌ Swallowing error details
|
|
|
|
**Reference**: `/docs/CODE_QUALITY.md#error-handling`
|
|
|
|
### 2.3 Async Patterns
|
|
|
|
**Principle**: All I/O operations are async
|
|
|
|
**Standard Pattern**:
|
|
```python
|
|
async def extract(self, source: str) -> TokenCollection:
|
|
# Async file reading
|
|
content = await asyncio.to_thread(Path(source).read_text)
|
|
return self._parse(content)
|
|
|
|
# Never block the event loop
|
|
# Use asyncio.to_thread for blocking operations
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Responsive API under load
|
|
- ✅ Efficient resource usage
|
|
- ✅ Better scalability
|
|
|
|
### 2.4 Docstrings & Comments
|
|
|
|
**Standard**: All modules, classes, and public functions have docstrings
|
|
|
|
```python
|
|
"""Module docstring explaining purpose.
|
|
|
|
This module handles token extraction from design files.
|
|
"""
|
|
|
|
class TokenSource(ABC):
|
|
"""Abstract base for design token sources.
|
|
|
|
Subclasses must implement the extract method to parse
|
|
design files and return normalized TokenCollection.
|
|
"""
|
|
|
|
@abstractmethod
|
|
async def extract(self, source: str) -> TokenCollection:
|
|
"""Extract tokens from a design source.
|
|
|
|
Args:
|
|
source: Path to design file or design system URL
|
|
|
|
Returns:
|
|
TokenCollection with normalized design tokens
|
|
|
|
Raises:
|
|
FileNotFoundError: If source file doesn't exist
|
|
ValueError: If source format is invalid
|
|
"""
|
|
```
|
|
|
|
**Comments**:
|
|
- Explain WHY, not WHAT (code shows what)
|
|
- Comment complex logic sections
|
|
- Mark TODOs with urgency level
|
|
- Update comments when code changes
|
|
|
|
**Reference**: `/docs/CODE_QUALITY.md#docstrings`
|
|
|
|
### 2.5 Type Hints & Mypy
|
|
|
|
**Principle**: 100% type hint coverage for public APIs
|
|
|
|
**Target Coverage**:
|
|
- ✅ Current: ~60% for internal code
|
|
- 🎯 Goal: 100% for all public functions
|
|
- 🎯 Goal: 80% overall codebase coverage
|
|
|
|
**Examples**:
|
|
```python
|
|
from typing import Optional, Dict, List, Union
|
|
from dataclasses import dataclass
|
|
|
|
async def process_tokens(
|
|
tokens: List[DesignToken],
|
|
merge_strategy: str = "first",
|
|
validate: bool = True
|
|
) -> Dict[str, DesignToken]:
|
|
"""Process design tokens."""
|
|
...
|
|
|
|
@dataclass
|
|
class Result:
|
|
success: bool
|
|
data: Optional[Dict] = None
|
|
error: Optional[str] = None
|
|
```
|
|
|
|
**Run Mypy**: `mypy tools/ --strict`
|
|
|
|
**Reference**: `/docs/CODE_QUALITY.md#type-hints`
|
|
|
|
---
|
|
|
|
## 3. Organism Framework Principles
|
|
|
|
DSS is a **living organism** with organ systems. Understand and design for these systems.
|
|
|
|
### 3.1 The Heart ❤️ - Database Layer
|
|
|
|
**Function**: Central source of truth, persistent storage
|
|
|
|
**Vital Signs**:
|
|
- Heart Rate = Database query response time
|
|
- Blood Pressure = Data write performance
|
|
- Rhythm = Regular sync cycles with external systems
|
|
|
|
**Health Indicators**:
|
|
- ✅ Consistent query latency
|
|
- ✅ No data corruption
|
|
- ✅ Reliable backups
|
|
- ❌ Slow queries = system sluggish
|
|
- ❌ Data inconsistency = system confused
|
|
- ❌ Missing backups = system vulnerable
|
|
|
|
**Design Principles**:
|
|
- **Immutability First**: Historical record of all changes
|
|
- **Strong Consistency**: No eventual consistency for design data
|
|
- **Backups**: Regular backups (weekly minimum)
|
|
- **Indexing**: Optimize common query patterns
|
|
- **Schema**: Never break existing contracts
|
|
|
|
**Reference**: `/docs/DSS_ORGANISM_GUIDE.md#1-the-heart`
|
|
|
|
### 3.2 The Brain 🧠 - Validators & Analysis
|
|
|
|
**Function**: Validate data quality, detect patterns, learn from data
|
|
|
|
**Neural Functions**:
|
|
- Pattern Recognition = Detecting design consistency issues
|
|
- Memory = Storing learned rules and patterns
|
|
- Decision-Making = Validation rules and quality gates
|
|
- Learning = Quick wins detection, improvement suggestions
|
|
|
|
**Health Indicators**:
|
|
- ✅ Validators catching errors early
|
|
- ✅ Analysis discovering patterns
|
|
- ✅ Smart suggestions for improvements
|
|
- ❌ Validators too strict = system constrained
|
|
- ❌ Validators too loose = garbage data accepted
|
|
- ❌ Analysis finds nothing = system unaware
|
|
|
|
**Design Principles**:
|
|
- **Validation First**: Catch errors at ingestion time
|
|
- **No False Positives**: Rules must catch real problems
|
|
- **Learning**: Improve detection over time
|
|
- **Transparency**: Users understand why validation fails
|
|
|
|
**Reference**: `/docs/DSS_ORGANISM_GUIDE.md#2-the-brain`
|
|
|
|
### 3.3 The Digestive System 🍽️ - Ingestion Pipeline
|
|
|
|
**Function**: Break down external design information into usable tokens
|
|
|
|
**Digestion Stages**:
|
|
1. **Intake** = Receiving raw design files
|
|
2. **Breakdown** = Parsing format-specific syntax
|
|
3. **Extraction** = Pulling out tokens/definitions
|
|
4. **Normalization** = Converting to standard token format
|
|
5. **Absorption** = Storing in the system
|
|
|
|
**Health Indicators**:
|
|
- ✅ Successfully parsing diverse input formats
|
|
- ✅ Zero loss of design information
|
|
- ✅ Fast processing time
|
|
- ❌ Failed parsing = maldigestion
|
|
- ❌ Lost data = nutrient malabsorption
|
|
- ❌ Slow processing = digestive dysfunction
|
|
|
|
**Design Principles**:
|
|
- **Format Agnostic**: Support CSS, SCSS, JSON, Figma, etc.
|
|
- **Zero Data Loss**: Never drop design information
|
|
- **Normalization**: Convert to standard token format immediately
|
|
- **Error Recovery**: Partial failures don't block entire ingestion
|
|
|
|
**Reference**: `/docs/DSS_ORGANISM_GUIDE.md#3-the-digestive-system`
|
|
|
|
### 3.4 The Nervous System 🔌 - APIs & Webhooks
|
|
|
|
**Function**: Perceive external world and respond automatically
|
|
|
|
**Neural Pathways**:
|
|
- **Sensory Input** = Figma webhooks detecting changes
|
|
- **Response** = Automatic sync and update triggers
|
|
- **Communication** = REST API, MCP interface
|
|
|
|
**Health Indicators**:
|
|
- ✅ Real-time responsiveness to Figma changes
|
|
- ✅ Reliable webhook delivery
|
|
- ✅ Available API endpoints
|
|
- ❌ Missed webhook events = delayed sync
|
|
- ❌ API downtime = external systems blocked
|
|
- ❌ Race conditions = data corruption
|
|
|
|
**Design Principles**:
|
|
- **Real-Time**: Respond to changes immediately
|
|
- **Reliable**: Guarantee event delivery (retries, idempotency)
|
|
- **Available**: 99.9% uptime for APIs
|
|
- **Observable**: Log all external interactions
|
|
|
|
**Reference**: `/docs/DSS_ORGANISM_GUIDE.md#4-the-nervous-system`
|
|
|
|
### 3.5 The Circulatory System 🩸 - Token Distribution
|
|
|
|
**Function**: Distribute tokens throughout the system to where they're needed
|
|
|
|
**Distribution Network**:
|
|
- Token → CSS Variables
|
|
- Token → Storybook Documentation
|
|
- Token → Component Generation
|
|
- Token → External Integrations
|
|
|
|
**Design Principles**:
|
|
- **Consistency**: Same token everywhere
|
|
- **Traceability**: Know where each token goes
|
|
- **Performance**: Efficient distribution, caching
|
|
- **Flexibility**: Multiple output formats
|
|
|
|
**Reference**: `/docs/DSS_ORGANISM_GUIDE.md` (distributed throughout docs)
|
|
|
|
---
|
|
|
|
## 4. Keyboard Accessibility
|
|
|
|
All interactive elements must be keyboard accessible. Users should be able to navigate and interact with the entire application using only the keyboard.
|
|
|
|
### Guidelines
|
|
|
|
- **Tabindex Management**
|
|
- Use `tabindex="0"` on all keyboard-accessible elements (buttons, links, inputs, custom components)
|
|
- Use `tabindex="-1"` for elements that shouldn't be in the tab order but need programmatic focus
|
|
- Never use positive tabindex values (e.g., `tabindex="1"`) - maintain natural DOM order
|
|
|
|
- **Focus Styling**
|
|
- Always implement `:focus-visible` CSS for keyboard users
|
|
- Ensure 3:1 contrast ratio for focus indicators
|
|
- Use outline or box-shadow, not display/visibility changes
|
|
- Apply consistently across all interactive elements
|
|
|
|
- **Keyboard Event Handlers**
|
|
- **Enter/Space**: Activate buttons and toggles
|
|
- **Escape**: Close modals, sidebars, and collapsible panels
|
|
- **Arrow Keys**: Navigate lists, tabs, and menus
|
|
- **Alt+Key**: Power user shortcuts for common actions
|
|
|
|
### Web Component Implementation
|
|
|
|
```javascript
|
|
class MyButton extends HTMLElement {
|
|
static get observedAttributes() {
|
|
return ['disabled', 'tabindex', 'aria-label', 'aria-expanded'];
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Handle Enter/Space for activation
|
|
this.keydownHandler = (e) => {
|
|
if ((e.key === 'Enter' || e.key === ' ') && !this.disabled) {
|
|
e.preventDefault();
|
|
this.shadowRoot.querySelector('button').click();
|
|
}
|
|
};
|
|
|
|
this.addEventListener('keydown', this.keydownHandler);
|
|
}
|
|
|
|
render() {
|
|
const tabindex = this.disabled ? '-1' : (this.getAttribute('tabindex') || '0');
|
|
// ... delegate tabindex to internal button
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.removeEventListener('keydown', this.keydownHandler);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/components/ds-button.js:79-96`
|
|
|
|
---
|
|
|
|
## 2. ARIA & Screen Reader Support
|
|
|
|
Screen reader users need semantic information about UI elements, their state, and purpose.
|
|
|
|
### Guidelines
|
|
|
|
- **Labeling**
|
|
- Always use `aria-label` on icon-only buttons
|
|
- Use `aria-label` on elements without visible text
|
|
- Example: `<button aria-label="Toggle theme">🌙</button>`
|
|
|
|
- **State Indication**
|
|
- Use `aria-expanded="true|false"` for toggles/collapsible elements
|
|
- Use `aria-pressed="true|false"` for toggle buttons
|
|
- Use `aria-invalid="true"` on form fields with errors
|
|
- Update ARIA attributes when state changes
|
|
|
|
- **Relationships**
|
|
- Use `aria-controls="id"` to link controls to their targets
|
|
- Use `aria-describedby="id"` to link inputs to error messages
|
|
- Use `aria-labelledby="id"` to link to headings when appropriate
|
|
- Use `aria-hidden="true"` on decorative elements (SVGs, icons)
|
|
|
|
- **Semantic Roles**
|
|
- Use native HTML elements when possible (button, input, link)
|
|
- Use `role="button"` only on divs styled as buttons (not ideal)
|
|
- Use `role="alert"` on error messages that appear dynamically
|
|
- Always prefer semantic HTML over generic divs
|
|
|
|
- **Error Handling**
|
|
- Generate unique IDs for error messages
|
|
- Link errors to inputs: `aria-describedby="error-{id}"`
|
|
- Use `role="alert"` so screen readers announce errors
|
|
- Set `aria-invalid="true"` on the input element
|
|
|
|
### Web Component Implementation
|
|
|
|
```javascript
|
|
class MyInput extends HTMLElement {
|
|
static get observedAttributes() {
|
|
return ['aria-label', 'aria-invalid', 'aria-describedby', 'error'];
|
|
}
|
|
|
|
render() {
|
|
const hasError = !!this.getAttribute('error');
|
|
const errorId = hasError ? `error-${Math.random().toString(36).substr(2, 9)}` : '';
|
|
const ariaInvalid = hasError ? 'aria-invalid="true"' : '';
|
|
const ariaDescribedBy = hasError ? `aria-describedby="${errorId}"` : '';
|
|
const ariaLabel = this.getAttribute('aria-label') || this.label || '';
|
|
|
|
this.shadowRoot.innerHTML = `
|
|
<input
|
|
aria-label="${ariaLabel}"
|
|
${ariaInvalid}
|
|
${ariaDescribedBy}
|
|
/>
|
|
${hasError ? `<p id="${errorId}" role="alert">${this.getAttribute('error')}</p>` : ''}
|
|
`;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/components/ds-input.js:172-240`
|
|
|
|
---
|
|
|
|
## 3. Web Component Lifecycle Management
|
|
|
|
Memory leaks from accumulated event listeners are a critical performance issue in SPAs. Web Components must properly manage their lifecycle.
|
|
|
|
### Guidelines
|
|
|
|
- **Connection/Disconnection**
|
|
- Implement both `connectedCallback()` and `disconnectedCallback()`
|
|
- `connectedCallback()`: Create shadow DOM, render, attach listeners
|
|
- `disconnectedCallback()`: Remove all listeners, clean up references
|
|
|
|
- **Event Listener Storage**
|
|
- Store listener references on `this` for later cleanup
|
|
- Never attach anonymous functions without storing references
|
|
- Example: `this.clickHandler = (e) => { ... }`
|
|
- Later: `element.removeEventListener('click', this.clickHandler)`
|
|
|
|
- **Attribute Observation**
|
|
- Implement `observedAttributes()` static getter
|
|
- Include all attributes that affect rendering: `disabled`, `tabindex`, ARIA attributes
|
|
- In `attributeChangedCallback()`, cleanup old listeners before re-rendering
|
|
|
|
### Implementation Pattern
|
|
|
|
```javascript
|
|
class MyComponent extends HTMLElement {
|
|
static get observedAttributes() {
|
|
return ['disabled', 'tabindex', 'aria-label'];
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.cleanupEventListeners();
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Store references
|
|
this.clickHandler = (e) => { /* ... */ };
|
|
this.focusHandler = (e) => { /* ... */ };
|
|
|
|
const button = this.shadowRoot.querySelector('button');
|
|
button.addEventListener('click', this.clickHandler);
|
|
this.addEventListener('focus', this.focusHandler);
|
|
}
|
|
|
|
cleanupEventListeners() {
|
|
const button = this.shadowRoot?.querySelector('button');
|
|
if (button && this.clickHandler) {
|
|
button.removeEventListener('click', this.clickHandler);
|
|
delete this.clickHandler;
|
|
}
|
|
if (this.focusHandler) {
|
|
this.removeEventListener('focus', this.focusHandler);
|
|
delete this.focusHandler;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/components/ds-button.js:27-113`
|
|
|
|
---
|
|
|
|
## 4. Event Delegation & Listener Accumulation Prevention
|
|
|
|
High-volume event listeners create memory pressure. Use event delegation and guard flags.
|
|
|
|
### Guidelines
|
|
|
|
- **Use Document-Level Delegation**
|
|
- Instead of: `buttons.forEach(b => b.addEventListener('click', handler))`
|
|
- Do: `document.addEventListener('click', (e) => { const btn = e.target.closest('[data-action]'); ... })`
|
|
- Benefit: One listener for N elements, regardless of count
|
|
|
|
- **Guard Flags**
|
|
- Prevent re-attachment on re-renders
|
|
- Check flag before attaching: `if (!this.listeners.hasActionListener) { ... }`
|
|
- Set flag after attachment: `this.listeners.hasActionListener = true;`
|
|
|
|
- **Data-Driven Actions**
|
|
- Use `data-action` attributes instead of inline onclick handlers
|
|
- Router actions through a single switch statement
|
|
- Example: `<button data-action="toggle-theme">Toggle</button>`
|
|
|
|
### Implementation Pattern
|
|
|
|
```javascript
|
|
attachEventHandlers() {
|
|
// Guard: only attach once, even if called multiple times
|
|
if (!this.listeners.hasActionListener) {
|
|
document.addEventListener('click', (e) => {
|
|
const btn = e.target.closest('[data-action]');
|
|
if (!btn) return;
|
|
|
|
const action = btn.dataset.action;
|
|
switch (action) {
|
|
case 'toggle-theme': this.toggleTheme(); break;
|
|
case 'load-audit': this.loadAuditLog(); break;
|
|
// ... more actions
|
|
}
|
|
});
|
|
|
|
this.listeners.hasActionListener = true;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/core/app.js:2202-2414`
|
|
|
|
---
|
|
|
|
## 5. Security: No Global State Pollution
|
|
|
|
Never expose critical classes or functions to `window`. Always use ES6 module imports.
|
|
|
|
### Guidelines
|
|
|
|
- **No Window Assignments**
|
|
- ❌ `window.app = app;`
|
|
- ❌ `window.themeManager = themeManager;`
|
|
- ✅ Use: `import app from './core/app.js';`
|
|
|
|
- **Risk Mitigation**
|
|
- Malicious/poorly-written third-party scripts can't hijack your code
|
|
- Prevents accidental global namespace pollution
|
|
- Makes code dependencies explicit and traceable
|
|
|
|
- **Development Debugging**
|
|
- If debugging requires window access, check environment first
|
|
- Example: Only expose in development mode
|
|
- Use: `if (process.env.NODE_ENV === 'development') { window.debug = {...}; }`
|
|
|
|
### Implementation
|
|
|
|
```javascript
|
|
// ❌ DON'T DO THIS
|
|
const app = new App();
|
|
window.app = app;
|
|
|
|
// ✅ DO THIS
|
|
const app = new App();
|
|
export default app;
|
|
|
|
// In other files:
|
|
import app from './app.js';
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/core/app.js` (removed window exposure), `/admin-ui/index.html:750`
|
|
|
|
---
|
|
|
|
## 6. Security: Sanitize HTML Input
|
|
|
|
Always sanitize HTML before insertion into the DOM. Use DOMPurify.
|
|
|
|
### Guidelines
|
|
|
|
- **Never Use Unsafe Insertion**
|
|
- ❌ `element.innerHTML = userInput;`
|
|
- ❌ `element.innerHTML = apiResponse;`
|
|
- ✅ Use: `sanitizeHtml()` and `setSafeHtml()`
|
|
|
|
- **DOMPurify Configuration**
|
|
- Default settings block dangerous attributes (onclick, onerror, etc.)
|
|
- Whitelist only necessary tags and attributes
|
|
- Strip scripts while preserving content
|
|
|
|
- **Content Sources Requiring Sanitization**
|
|
- User input (forms, search, comments)
|
|
- API responses (markdown, rich text, HTML)
|
|
- External data (imported content, integrations)
|
|
|
|
### Implementation
|
|
|
|
```javascript
|
|
import { setSafeHtml, sanitizeHtml } from './sanitizer.js';
|
|
|
|
// Safe HTML insertion
|
|
const htmlContent = `<h2>${name}</h2><p>${description}</p>`;
|
|
setSafeHtml(element, htmlContent);
|
|
|
|
// Sanitize and retrieve
|
|
const clean = sanitizeHtml(userInput);
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/core/sanitizer.js`, `/admin-ui/js/core/app.js:28`
|
|
|
|
---
|
|
|
|
## 7. Non-Invasive Network Monitoring
|
|
|
|
Never monkey-patch native APIs. Use non-invasive alternatives.
|
|
|
|
### Guidelines
|
|
|
|
- **Don't Monkey-Patch Global Functions**
|
|
- ❌ `window.fetch = async (...) => { ... }`
|
|
- ❌ Breaks third-party integrations
|
|
- ❌ Causes race conditions
|
|
|
|
- **Use Platform APIs Instead**
|
|
- ✅ `PerformanceObserver` for network monitoring
|
|
- ✅ Browser Performance API for metrics
|
|
- ✅ Event listeners on `window` for errors
|
|
|
|
- **Graceful Degradation**
|
|
- Check if API is available: `if ('PerformanceObserver' in window)`
|
|
- Try/catch for unsupported browsers
|
|
- Continue functionality if monitoring unavailable
|
|
|
|
### Implementation
|
|
|
|
```javascript
|
|
captureNetworkActivity() {
|
|
if ('PerformanceObserver' in window) {
|
|
try {
|
|
const observer = new PerformanceObserver((list) => {
|
|
for (const entry of list.getEntries()) {
|
|
if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {
|
|
this.log('network', entry.initiatorType, `${entry.name}`, {
|
|
duration: entry.duration,
|
|
transferSize: entry.transferSize
|
|
});
|
|
}
|
|
}
|
|
});
|
|
observer.observe({ entryTypes: ['resource'] });
|
|
} catch (e) {
|
|
// Graceful degradation - network logging won't work
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/core/browser-logger.js:134-162`
|
|
|
|
---
|
|
|
|
## 8. Focus Management
|
|
|
|
Maintain user's context by managing focus properly during UI updates.
|
|
|
|
### Guidelines
|
|
|
|
- **Focus Delegation**
|
|
- Web Components should delegate focus to internal focusable element
|
|
- Implement focus() method that focuses internal input/button
|
|
|
|
- **Focus Restoration**
|
|
- Store focused element before UI change
|
|
- Restore focus after update if appropriate
|
|
- Announce focus changes to screen readers
|
|
|
|
- **Aria-Expanded Updates**
|
|
- Update `aria-expanded` when toggles change state
|
|
- Ensures screen readers announce state changes
|
|
- Example: `toggleBtn.setAttribute('aria-expanded', isOpen);`
|
|
|
|
### Implementation
|
|
|
|
```javascript
|
|
class MyComponent extends HTMLElement {
|
|
focus() {
|
|
// Delegate focus to internal input
|
|
this.shadowRoot.querySelector('input')?.focus();
|
|
}
|
|
|
|
togglePanel() {
|
|
const isOpen = !this.isOpen;
|
|
this.isOpen = isOpen;
|
|
|
|
// Update ARIA state
|
|
this.setAttribute('aria-expanded', isOpen);
|
|
|
|
// Restore focus to toggle button after panel opens
|
|
if (isOpen) {
|
|
this.focus();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/core/app.js:2495-2502`
|
|
|
|
---
|
|
|
|
## 9. Semantic HTML Over Custom Divs
|
|
|
|
Use native HTML elements. They provide built-in accessibility.
|
|
|
|
### Guidelines
|
|
|
|
- **Prefer Native Elements**
|
|
- ✅ `<button>` instead of `<div role="button">`
|
|
- ✅ `<input>` instead of `<div contenteditable>`
|
|
- ✅ `<select>` instead of custom dropdown
|
|
- ✅ `<label>` instead of span with onclick
|
|
|
|
- **When Custom Elements Are Necessary**
|
|
- Implement proper ARIA roles and attributes
|
|
- Ensure keyboard interaction works identically to native
|
|
- Test with screen readers and keyboard-only navigation
|
|
|
|
- **Benefits of Native HTML**
|
|
- Built-in keyboard support
|
|
- Screen reader integration
|
|
- Mobile device handling
|
|
- Browser compatibility
|
|
- User expectations met
|
|
|
|
---
|
|
|
|
## 10. Observable Attributes for Web Components
|
|
|
|
Web Components must expose all relevant attributes in `observedAttributes()` to respond to changes.
|
|
|
|
### Guidelines
|
|
|
|
- **Include All State Attributes**
|
|
- `disabled`, `aria-label`, `aria-expanded`, `aria-pressed`
|
|
- `tabindex`, `value`, `error`, `variant`
|
|
- Any attribute that affects rendering or behavior
|
|
|
|
- **Cleanup in attributeChangedCallback**
|
|
- Remove old listeners before re-rendering
|
|
- Call `cleanupEventListeners()` first
|
|
- Then call `render()` and `setupEventListeners()`
|
|
|
|
- **Re-render Efficiently**
|
|
- Only re-render if shadowRoot has content
|
|
- Check `if (this.shadowRoot.innerHTML)`
|
|
- Avoid re-rendering on initial connection
|
|
|
|
### Implementation
|
|
|
|
```javascript
|
|
class MyComponent extends HTMLElement {
|
|
static get observedAttributes() {
|
|
// Include ALL attributes that affect rendering
|
|
return ['disabled', 'tabindex', 'aria-label', 'aria-expanded', 'aria-invalid', 'error'];
|
|
}
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
if (this.shadowRoot.innerHTML && oldValue !== newValue) {
|
|
// Cleanup first
|
|
this.cleanupEventListeners();
|
|
// Then re-render
|
|
this.render();
|
|
this.setupEventListeners();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Reference**: `/admin-ui/js/components/ds-input.js:39-52`
|
|
|
|
---
|
|
|
|
## 11. Logging with Organism Awareness
|
|
|
|
Use structured logging that helps understand system state and consciousness.
|
|
|
|
### Guidelines
|
|
|
|
- **Log Categories** (Organ Systems)
|
|
- `'heart'` - Database operations and data persistence
|
|
- `'brain'` - Validation, analysis, and decision making
|
|
- `'nervous'` - API calls, webhooks, communication
|
|
- `'digestive'` - Data ingestion, parsing, transformation
|
|
- `'endocrine'` - Theme system and configuration
|
|
- `'immune'` - Validation, error detection, security
|
|
|
|
- **Log Levels**
|
|
- `DEBUG` - Deep thought, internal analysis
|
|
- `INFO` - Organism awareness, normal operations
|
|
- `WARN` - Symptom detection, unusual conditions
|
|
- `ERROR` - Immune alert, critical threats
|
|
|
|
- **Structure**
|
|
- Always include: timestamp, level, category, message, data
|
|
- Use consistent naming for categories
|
|
- Include relevant IDs (user, session, operation)
|
|
|
|
**Reference**: `/admin-ui/js/core/logger.js:27-152`
|
|
|
|
---
|
|
|
|
## 12. Error Handling & User Feedback
|
|
|
|
Provide clear feedback for errors while maintaining security.
|
|
|
|
### Guidelines
|
|
|
|
- **User-Facing Messages**
|
|
- Be specific but not technical: "Unable to save changes" not "500 Internal Server Error"
|
|
- Suggest action: "Please try again" or "Contact support"
|
|
- Announce errors to screen readers with `role="alert"`
|
|
|
|
- **Internal Logging**
|
|
- Log full error details internally for debugging
|
|
- Include stack traces, request data, timestamps
|
|
- Never expose sensitive information to users
|
|
|
|
- **Notification Timing**
|
|
- Success: 3-5 seconds
|
|
- Warnings: 5-7 seconds
|
|
- Errors: Persistent until user dismisses
|
|
|
|
**Reference**: `/admin-ui/js/core/messaging.js`
|
|
|
|
---
|
|
|
|
## Summary: Accessibility Checklist
|
|
|
|
Before shipping a component, verify:
|
|
|
|
- [ ] All interactive elements have `tabindex="0"` or semantic focus
|
|
- [ ] All icon buttons have `aria-label`
|
|
- [ ] All toggles/collapsibles have `aria-expanded`
|
|
- [ ] All form errors have `aria-invalid` and `aria-describedby`
|
|
- [ ] `:focus-visible` CSS is present for keyboard users
|
|
- [ ] Keyboard shortcuts work (Enter, Space, Escape, Arrows)
|
|
- [ ] `disconnectedCallback()` removes all event listeners
|
|
- [ ] Event listeners are stored as `this.handler` for cleanup
|
|
- [ ] No HTML is inserted via `innerHTML` without sanitization
|
|
- [ ] No functions/classes exposed to `window`
|
|
- [ ] Decorative SVGs have `aria-hidden="true"`
|
|
- [ ] Error messages have `role="alert"`
|
|
- [ ] Uses semantic HTML where possible
|
|
- [ ] Works with keyboard-only navigation
|
|
- [ ] Works with screen readers (test with NVDA or JAWS)
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
|
- [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)
|
|
- [Web Components Best Practices](https://web.dev/articles/custom-elements-best-practices)
|
|
- [Focus Management](https://www.smashingmagazine.com/2019/06/focus-management-keyboard-navigation/)
|
|
- [DOMPurify Documentation](https://github.com/cure53/DOMPurify)
|