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
28 KiB
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
- Architectural Principles
- Code Quality Standards
- Organism Framework Principles
- Keyboard Accessibility
- Web Component Standards
- Security Guidelines
- Event Handling & Memory Management
- Testing & Quality Assurance
- Documentation Standards
- 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):
- Storage Layer - SQLite, file system, cache
- Core Domain Layer - Token models, merge logic, validation
- Ingestion Layer - Source parsers (CSS, SCSS, Figma, etc.)
- Analysis Layer - Project scanning, quick wins, metrics
- Generation Layer - Storybook, component generation, output
- 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
class TokenMerger:
def __init__(self, strategy: MergeStrategy):
self.strategy = strategy # FIRST, LAST, PREFER_FIGMA, etc.
Abstract Base Classes - For extension points
class TokenSource(ABC):
@abstractmethod
async def extract(self, source: str) -> TokenCollection:
pass
Data Classes - For immutable domain models
@dataclass(frozen=True)
class DesignToken:
name: str
value: str
type: TokenType
Dependency Injection - Explicit dependencies
# 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:
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:
# 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 (
passwithout logging) - ❌ Generic
Exceptioncatching - ❌ Swallowing error details
Reference: /docs/CODE_QUALITY.md#error-handling
2.3 Async Patterns
Principle: All I/O operations are async
Standard Pattern:
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
"""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:
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:
- Intake = Receiving raw design files
- Breakdown = Parsing format-specific syntax
- Extraction = Pulling out tokens/definitions
- Normalization = Converting to standard token format
- 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
- Use
-
Focus Styling
- Always implement
:focus-visibleCSS 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
- Always implement
-
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
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-labelon icon-only buttons - Use
aria-labelon elements without visible text - Example:
<button aria-label="Toggle theme">🌙</button>
- Always use
-
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
- Use
-
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)
- Use
-
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
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()anddisconnectedCallback() connectedCallback(): Create shadow DOM, render, attach listenersdisconnectedCallback(): Remove all listeners, clean up references
- Implement both
-
Event Listener Storage
- Store listener references on
thisfor later cleanup - Never attach anonymous functions without storing references
- Example:
this.clickHandler = (e) => { ... } - Later:
element.removeEventListener('click', this.clickHandler)
- Store listener references on
-
Attribute Observation
- Implement
observedAttributes()static getter - Include all attributes that affect rendering:
disabled,tabindex, ARIA attributes - In
attributeChangedCallback(), cleanup old listeners before re-rendering
- Implement
Implementation Pattern
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
- Instead of:
-
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-actionattributes instead of inline onclick handlers - Router actions through a single switch statement
- Example:
<button data-action="toggle-theme">Toggle</button>
- Use
Implementation Pattern
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
// ❌ 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()andsetSafeHtml()
- ❌
-
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
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
- ✅
PerformanceObserverfor network monitoring - ✅ Browser Performance API for metrics
- ✅ Event listeners on
windowfor errors
- ✅
-
Graceful Degradation
- Check if API is available:
if ('PerformanceObserver' in window) - Try/catch for unsupported browsers
- Continue functionality if monitoring unavailable
- Check if API is available:
Implementation
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-expandedwhen toggles change state - Ensures screen readers announce state changes
- Example:
toggleBtn.setAttribute('aria-expanded', isOpen);
- Update
Implementation
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-pressedtabindex,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()andsetupEventListeners()
-
Re-render Efficiently
- Only re-render if shadowRoot has content
- Check
if (this.shadowRoot.innerHTML) - Avoid re-rendering on initial connection
Implementation
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 analysisINFO- Organism awareness, normal operationsWARN- Symptom detection, unusual conditionsERROR- 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-invalidandaria-describedby :focus-visibleCSS is present for keyboard users- Keyboard shortcuts work (Enter, Space, Escape, Arrows)
disconnectedCallback()removes all event listeners- Event listeners are stored as
this.handlerfor cleanup - No HTML is inserted via
innerHTMLwithout 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)