Development Tools and Debugging
1. HTML Validation and W3C Compliance
| Tool/Service | URL/Usage | Description | Best For |
|---|---|---|---|
| W3C Markup Validation | validator.w3.org |
Official W3C HTML validator | Standards compliance, catching syntax errors |
| Nu Html Checker (v.Nu) | validator.w3.org/nu/ |
Modern HTML5 validator | HTML5 features, living standard compliance |
| HTML Validator Extension | Browser extension (Chrome, Firefox) | Real-time validation in browser | Development workflow, instant feedback |
| html-validate (npm) | npm install -g html-validate |
CLI/API validator with custom rules | CI/CD pipelines, build automation |
| htmlhint (npm) | npm install -g htmlhint |
Fast HTML linter with configurable rules | Static analysis, team style guides |
| VS Code Extensions | HTMLHint, W3C Validation | IDE-integrated validation | Real-time editing, inline errors |
| AMP Validator | validator.ampproject.org |
Validate AMP HTML pages | AMP-specific compliance |
| Structured Data Testing | search.google.com/test/rich-results |
Validate schema.org markup | SEO, rich snippets validation |
Example: HTML validation workflow
<!-- Common validation errors to avoid -->
<!-- ERROR: Missing DOCTYPE -->
<html> <!-- ✗ Should start with <!DOCTYPE html> -->
<!-- ERROR: Missing required attributes -->
<img src="image.jpg"> <!-- ✗ Missing alt attribute -->
<img src="image.jpg" alt="Description"> <!-- ✓ Correct -->
<!-- ERROR: Duplicate IDs -->
<div id="content">First</div>
<div id="content">Second</div> <!-- ✗ ID must be unique -->
<!-- ERROR: Improper nesting -->
<p>Paragraph <div>with div</div></p> <!-- ✗ Block in inline -->
<p>Paragraph <span>with span</span></p> <!-- ✓ Correct -->
<!-- ERROR: Unclosed tags -->
<div>
<p>Paragraph
</div> <!-- ✗ Missing </p> -->
<!-- ERROR: Invalid attribute values -->
<input type="text" required="true"> <!-- ✗ Boolean attribute with value -->
<input type="text" required> <!-- ✓ Correct -->
<!-- ERROR: Obsolete elements -->
<center>Text</center> <!-- ✗ Obsolete -->
<div style="text-align: center">Text</div> <!-- ✓ Use CSS -->
<!-- ERROR: Missing charset -->
<head>
<title>Page</title> <!-- ✗ Missing charset declaration -->
</head>
<head>
<meta charset="UTF-8"> <!-- ✓ Should be first in head -->
<title>Page</title>
</head>
Example: Using html-validate in project
<!-- Install html-validate -->
npm install --save-dev html-validate
<!-- Create .htmlvalidate.json config -->
{
"extends": ["html-validate:recommended"],
"rules": {
"close-order": "error",
"void-style": ["error", "selfclose"],
"require-sri": "warn",
"no-inline-style": "warn",
"attribute-boolean-style": "error",
"attr-case": ["error", "lowercase"],
"id-pattern": ["error", "kebab-case"],
"no-deprecated-attr": "error",
"require-heading": "warn"
}
}
<!-- Run validation -->
npx html-validate "src/**/*.html"
<!-- Add to package.json scripts -->
{
"scripts": {
"validate": "html-validate 'src/**/*.html'",
"validate:fix": "html-validate --fix 'src/**/*.html'"
}
}
<!-- Use in CI/CD (GitHub Actions) -->
name: Validate HTML
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run validate
Validation Best Practices: Validate early and often during development. Fix errors in order
(DOCTYPE, charset, structure, then attributes). Use automated validation in CI/CD. Remember validation ensures
correct syntax, not good design or accessibility.
2. Browser DevTools for HTML Debugging
| DevTools Feature | Keyboard Shortcut | Description | Use Case |
|---|---|---|---|
| Inspect Element | Ctrl+Shift+C / Cmd+Shift+C |
Select element on page to inspect | Quick navigation to element in DOM tree |
| Elements Panel | F12 then Elements tab |
View and edit DOM structure and CSS | Live HTML/CSS editing, debugging layout |
| Edit as HTML | Right-click → Edit as HTML | Edit entire element structure | Test HTML changes, fix nesting issues |
| Break on Attribute Changes | Right-click → Break on → attribute modifications | Pause when element attributes change | Debug dynamic attribute changes |
| Break on Subtree Modifications | Right-click → Break on → subtree modifications | Pause when child elements added/removed | Debug dynamic content injection |
| Break on Node Removal | Right-click → Break on → node removal | Pause when element is removed from DOM | Find what's removing elements |
| Computed Styles | Elements → Computed tab | See final computed CSS values | Debug CSS specificity, inheritance |
| Event Listeners | Elements → Event Listeners tab | View all event handlers on element | Debug event handling, remove listeners |
| Accessibility Tree | Elements → Accessibility tab | View accessibility properties and ARIA | Debug screen reader issues |
| DOM Breakpoints | Elements → DOM Breakpoints sidebar | List all DOM modification breakpoints | Manage multiple breakpoints |
| Search in DOM | Ctrl+F / Cmd+F in Elements |
Search by text, selector, or XPath | Find elements quickly |
| Copy as HTML/Selector | Right-click → Copy | Copy element HTML, selector, XPath, etc. | Share code, create selectors |
Example: DevTools debugging techniques
<!-- Console commands for HTML debugging -->
// Select element by CSS selector
$('div.container') // First match
$('div.container') // All matches (array)
$x('//div[@class="container"]') // XPath selector
// Inspect selected element
$0 // Currently selected element in Elements panel
$1 // Previously selected element
$2 // Element before that
// Get element properties
$0.innerHTML
$0.outerHTML
$0.attributes
$0.classList
$0.dataset // data-* attributes
// Monitor events
monitorEvents($0) // All events
monitorEvents($0, 'click') // Specific event
monitorEvents($0, ['click', 'keydown']) // Multiple events
unmonitorEvents($0) // Stop monitoring
// Inspect element
inspect($('button')) // Select element in Elements panel
// Get event listeners
getEventListeners($0) // All listeners on element
// Copy to clipboard
copy($0.outerHTML) // Copy element HTML
copy($('a').map(a => a.href)) // Copy all link URLs
// Debug DOM modifications
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log('Type:', mutation.type);
console.log('Added:', mutation.addedNodes);
console.log('Removed:', mutation.removedNodes);
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
characterData: true
});
// Stop observing
observer.disconnect();
// Find elements without required attributes
$('img:not([alt])').length // Images missing alt
$('a:not([href])').length // Links without href
$('button:not([type])').length // Buttons without type
// Find accessibility issues
$('[role]:not([aria-label]):not([aria-labelledby])')
$('div[onclick]') // Divs with click handlers (bad for a11y)
// Performance: Count DOM elements
document.querySelectorAll('*').length
$('*').length // Same
// Find duplicate IDs
const ids = [...$('[id]')].map(el => el.id);
ids.filter((id, i) => ids.indexOf(id) !== i); // Duplicates
| DevTool | Chrome DevTools | Firefox DevTools | Safari Web Inspector |
|---|---|---|---|
| Open DevTools | F12 / Cmd+Opt+I | F12 / Cmd+Opt+I | Cmd+Opt+I |
| Inspect Element | Ctrl+Shift+C | Ctrl+Shift+C | Cmd+Shift+C |
| Console | Ctrl+Shift+J | Ctrl+Shift+K | Cmd+Opt+C |
| 3D View | ✓ Layers tab | ✓ 3D View | ✓ Layers sidebar |
| Grid Inspector | ✓ Layout pane | ✓ Grid badge | ✓ Layout sidebar |
| Flexbox Inspector | ✓ Layout pane | ✓ Flexbox badge | ✓ Layout sidebar |
3. Accessibility Testing Tools
| Tool | Type | Description | Key Features |
|---|---|---|---|
| axe DevTools | Browser Extension | Automated accessibility testing in browser | WCAG 2.1/2.2 compliance, guided fixes |
| WAVE | Browser Extension / Web Service | Visual feedback on accessibility issues | Color-coded annotations, contrast checker |
| Lighthouse Accessibility | Built into Chrome DevTools | Automated accessibility audit | Score 0-100, actionable recommendations |
| NVDA / JAWS | Screen Reader Software | Test with actual screen reader | Real user experience, keyboard navigation |
| VoiceOver (macOS/iOS) | Built-in Screen Reader | Apple's screen reader for testing | Cmd+F5 to enable, test on Mac/iPhone |
| pa11y | CLI / npm package | Automated testing in CI/CD | Command-line testing, integration ready |
| axe-core | JavaScript Library | Programmatic accessibility testing | Integrate in tests, custom rules |
| Color Contrast Analyzer | Desktop App / Extension | Check color contrast ratios | WCAG AA/AAA compliance checking |
| Accessibility Insights | Browser Extension (Microsoft) | Comprehensive accessibility testing | Fast Pass, Assessment, Tab stops visualization |
| HeadingsMap | Browser Extension | Visualize heading structure | Document outline, heading hierarchy |
Example: Accessibility testing workflow
<!-- Install and use axe-core for automated testing -->
npm install --save-dev axe-core
<!-- HTML page to test -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Accessibility Test</title>
</head>
<body>
<!-- Include axe-core -->
<script src="node_modules/axe-core/axe.min.js"></script>
<script>
// Run accessibility audit
axe.run().then(results => {
if (results.violations.length) {
console.error('Accessibility Violations:', results.violations);
results.violations.forEach(violation => {
console.group(`${violation.impact}: ${violation.help}`);
console.log('Description:', violation.description);
console.log('Help URL:', violation.helpUrl);
violation.nodes.forEach(node => {
console.log('Element:', node.html);
console.log('Selector:', node.target);
console.log('Fix:', node.failureSummary);
});
console.groupEnd();
});
} else {
console.log('✓ No accessibility violations found!');
}
// Log passes
console.log('Passed checks:', results.passes.length);
// Log incomplete (needs manual review)
console.log('Manual review needed:', results.incomplete.length);
});
</script>
</body>
</html>
<!-- Use pa11y for CI/CD -->
npm install -g pa11y
# Test single page
pa11y https://example.com
# Test with specific standard
pa11y --standard WCAG2AA https://example.com
# Generate report
pa11y --reporter json https://example.com > report.json
# Test multiple pages
pa11y-ci --sitemap https://example.com/sitemap.xml
<!-- package.json script -->
{
"scripts": {
"test:a11y": "pa11y-ci --config .pa11yci.json"
}
}
<!-- .pa11yci.json config -->
{
"defaults": {
"standard": "WCAG2AA",
"runners": ["axe", "htmlcs"]
},
"urls": [
"http://localhost:3000/",
"http://localhost:3000/about",
"http://localhost:3000/contact"
]
}
Manual Testing Checklist:
- Keyboard navigation (Tab, Shift+Tab, Enter, Space, arrows)
- Screen reader testing (NVDA, JAWS, VoiceOver)
- Zoom to 200% (text should reflow, no horizontal scroll)
- Color contrast (4.5:1 normal text, 3:1 large text)
- Forms with screen reader (labels, errors, instructions)
- Images (alt text descriptive, decorative marked alt="")
Common A11y Issues:
- Missing alt attributes on images
- Form inputs without labels
- Low color contrast ratios
- Keyboard focus not visible
- Non-semantic elements for interactive content
- Missing ARIA labels on icon buttons
4. Performance Profiling and Lighthouse
| Tool | Access | Description | Key Metrics |
|---|---|---|---|
| Lighthouse | Chrome DevTools → Lighthouse | Automated performance, accessibility, SEO audit | Performance score, FCP, LCP, CLS, TBT |
| PageSpeed Insights | pagespeed.web.dev |
Online Lighthouse with field data | Lab + Real-user Core Web Vitals |
| WebPageTest | webpagetest.org |
Advanced performance testing | Waterfall, filmstrip, video playback |
| Chrome Performance Panel | DevTools → Performance | Record runtime performance | FPS, CPU usage, network activity |
| Chrome Coverage | DevTools → Coverage | Find unused CSS/JS | Percentage of unused code |
| Network Panel | DevTools → Network | Analyze resource loading | Request timing, waterfall, size |
| Rendering Panel | DevTools → More tools → Rendering | Visualize layout shifts, paint flashing | CLS debugging, paint areas |
| Lighthouse CI | npm package for CI/CD | Automated Lighthouse in build pipeline | Performance budgets, regression detection |
Example: Lighthouse and performance testing
<!-- Run Lighthouse programmatically -->
npm install -g lighthouse
# Test URL
lighthouse https://example.com --output html --output-path report.html
# Test with specific categories
lighthouse https://example.com --only-categories=performance,accessibility
# Test mobile
lighthouse https://example.com --preset=desktop
lighthouse https://example.com --preset=mobile # default
# Custom Chrome flags
lighthouse https://example.com --chrome-flags="--headless --no-sandbox"
# Budget.json for performance budgets
lighthouse https://example.com --budget-path=budget.json
<!-- budget.json example -->
[
{
"path": "/*",
"timings": [
{"metric": "first-contentful-paint", "budget": 2000},
{"metric": "largest-contentful-paint", "budget": 2500},
{"metric": "cumulative-layout-shift", "budget": 0.1},
{"metric": "total-blocking-time", "budget": 300}
],
"resourceSizes": [
{"resourceType": "script", "budget": 300},
{"resourceType": "stylesheet", "budget": 100},
{"resourceType": "image", "budget": 500},
{"resourceType": "document", "budget": 50},
{"resourceType": "font", "budget": 100},
{"resourceType": "total", "budget": 1000}
],
"resourceCounts": [
{"resourceType": "script", "budget": 10},
{"resourceType": "stylesheet", "budget": 5},
{"resourceType": "image", "budget": 20},
{"resourceType": "third-party", "budget": 5}
]
}
]
<!-- Lighthouse CI in GitHub Actions -->
name: Lighthouse CI
on: [push]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run build
- run: npm install -g @lhci/cli
- run: lhci autorun
<!-- lighthouserc.json config -->
{
"ci": {
"collect": {
"url": ["http://localhost:3000/"],
"numberOfRuns": 3
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}],
"categories:best-practices": ["error", {"minScore": 0.9}],
"categories:seo": ["error", {"minScore": 0.9}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
| Core Web Vital | Metric | Good | Needs Improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Loading performance | <2.5s | 2.5s - 4s | >4s |
| FID (First Input Delay) | Interactivity | <100ms | 100ms - 300ms | >300ms |
| CLS (Cumulative Layout Shift) | Visual stability | <0.1 | 0.1 - 0.25 | >0.25 |
| INP (Interaction to Next Paint) NEW | Responsiveness (replacing FID) | <200ms | 200ms - 500ms | >500ms |
| FCP (First Contentful Paint) | Perceived load speed | <1.8s | 1.8s - 3s | >3s |
| TTFB (Time to First Byte) | Server response time | <800ms | 800ms - 1800ms | >1800ms |
5. HTML Linting and Code Quality
| Tool | Installation | Description | Configuration |
|---|---|---|---|
| HTMLHint | npm install -g htmlhint |
Static code analysis for HTML | .htmlhintrc file |
| html-validate | npm install -g html-validate |
Offline HTML5 validator | .htmlvalidate.json |
| Prettier | npm install -g prettier |
Opinionated code formatter | .prettierrc |
| EditorConfig | IDE plugin | Consistent coding styles across editors | .editorconfig |
| Stylelint | npm install -g stylelint |
Lint CSS in <style> tags | .stylelintrc |
| ESLint (HTML plugin) | npm install eslint-plugin-html |
Lint JavaScript in <script> tags | .eslintrc with html plugin |
| linthtml | npm install -g @linthtml/linthtml |
Fork of HTMLHint with active maintenance | .linthtmlrc |
| SonarQube/SonarLint | IDE extension / Server | Code quality and security analysis | sonar-project.properties |
Example: Linting configuration
<!-- .htmlhintrc configuration -->
{
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"attr-value-not-empty": false,
"attr-no-duplication": true,
"doctype-first": true,
"tag-pair": true,
"tag-self-close": false,
"spec-char-escape": true,
"id-unique": true,
"src-not-empty": true,
"title-require": true,
"alt-require": true,
"doctype-html5": true,
"id-class-value": "dash",
"style-disabled": false,
"inline-style-disabled": false,
"inline-script-disabled": false,
"space-tab-mixed-disabled": "space",
"id-class-ad-disabled": true,
"href-abs-or-rel": false,
"attr-unsafe-chars": true
}
<!-- .prettierrc for HTML formatting -->
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"trailingComma": "none",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"endOfLine": "lf"
}
<!-- .editorconfig for consistent style -->
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.html]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
<!-- package.json scripts -->
{
"scripts": {
"lint:html": "htmlhint 'src/**/*.html'",
"lint:validate": "html-validate 'src/**/*.html'",
"format": "prettier --write 'src/**/*.html'",
"format:check": "prettier --check 'src/**/*.html'",
"lint": "npm run lint:html && npm run lint:validate",
"precommit": "npm run lint && npm run format:check"
}
}
<!-- VSCode settings.json for auto-formatting -->
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"html.validate.scripts": true,
"html.validate.styles": true,
"htmlhint.enable": true,
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true
}
<!-- Run linting -->
htmlhint src/**/*.html
html-validate src/**/*.html
prettier --check src/**/*.html
<!-- Auto-fix issues -->
prettier --write src/**/*.html
html-validate --fix src/**/*.html
Linting Best Practices: Configure linters early in project. Use pre-commit hooks (husky +
lint-staged) to enforce quality. Combine multiple tools (HTMLHint for errors, Prettier for formatting). Share
configs across team via .editorconfig. Run linting in CI/CD to catch issues before merge.
6. Cross-browser Testing Strategies
| Strategy/Tool | Type | Description | Best For |
|---|---|---|---|
| BrowserStack | Cloud Testing Platform | Real devices and browsers in the cloud | Comprehensive testing, mobile devices |
| Sauce Labs | Cloud Testing Platform | Automated and manual cross-browser testing | CI/CD integration, Selenium tests |
| LambdaTest | Cloud Testing Platform | Live interactive and automated testing | Real-time debugging, screenshots |
| Can I Use | Reference Website | Browser feature support tables | Feature detection, planning support |
| Modernizr | JavaScript Library | Feature detection library | Polyfill loading, conditional features |
| Playwright | Testing Framework | Cross-browser automation (Chromium, Firefox, WebKit) | E2E testing, screenshot comparison |
| Selenium | Testing Framework | Browser automation across platforms | Legacy support, wide browser coverage |
| Cypress | Testing Framework | Modern E2E testing (Chrome, Firefox, Edge) | Fast testing, developer experience |
| Percy | Visual Testing | Visual regression testing | UI consistency, CSS changes |
| Polyfill.io | CDN Service | Automatic polyfills based on user agent | Progressive enhancement, legacy browsers |
Example: Cross-browser testing setup
<!-- Feature detection with Modernizr -->
<script src="https://cdn.jsdelivr.net/npm/modernizr@3.12.0/modernizr.min.js"></script>
<script>
// Check feature support
if (Modernizr.flexbox) {
console.log('Flexbox supported');
} else {
console.log('Flexbox NOT supported - load polyfill');
// Load flexbox polyfill
}
if (Modernizr.webp) {
// Use WebP images
document.documentElement.classList.add('webp');
} else {
// Fallback to JPEG/PNG
document.documentElement.classList.add('no-webp');
}
</script>
<!-- Polyfill.io for automatic polyfills -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=default,es2015,es2016,es2017"></script>
<!-- Conditional loading based on feature detection -->
<script>
// Load polyfill only if needed
if (!window.Promise) {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js';
document.head.appendChild(script);
}
// IntersectionObserver polyfill
if (!('IntersectionObserver' in window)) {
const script = document.createElement('script');
script.src = 'https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver';
document.head.appendChild(script);
}
</script>
<!-- Playwright test example -->
// playwright.config.js
module.exports = {
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' }
},
{
name: 'firefox',
use: { browserName: 'firefox' }
},
{
name: 'webkit',
use: { browserName: 'webkit' }
}
]
};
// test.spec.js
const { test, expect } = require('@playwright/test');
test.describe('Cross-browser HTML tests', () => {
test('page title is correct', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveTitle('Example Domain');
});
test('all images have alt attributes', async ({ page }) => {
await page.goto('https://example.com');
const imagesWithoutAlt = await page.$('img:not([alt])');
expect(imagesWithoutAlt.length).toBe(0);
});
test('form submits correctly', async ({ page }) => {
await page.goto('https://example.com/form');
await page.fill('input[name="email"]', 'test@example.com');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/.*success/);
});
});
<!-- BrowserStack integration example -->
// wdio.conf.js (WebDriverIO with BrowserStack)
exports.config = {
user: process.env.BROWSERSTACK_USERNAME,
key: process.env.BROWSERSTACK_ACCESS_KEY,
capabilities: [
{
browserName: 'Chrome',
browserVersion: 'latest',
'bstack:options': {
os: 'Windows',
osVersion: '11'
}
},
{
browserName: 'Safari',
browserVersion: 'latest',
'bstack:options': {
os: 'OS X',
osVersion: 'Monterey'
}
},
{
browserName: 'Edge',
browserVersion: 'latest',
'bstack:options': {
os: 'Windows',
osVersion: '10'
}
}
],
services: ['browserstack']
};
| Browser | Market Share (2024) | Testing Priority | Common Issues |
|---|---|---|---|
| Chrome | ~65% | High - Primary | Usually most standards-compliant |
| Safari | ~20% | High - iOS critical | CSS Grid bugs, date input format, WebP support |
| Edge | ~5% | Medium - Chromium-based | Similar to Chrome (Chromium engine) |
| Firefox | ~3% | Medium - Different engine | Flexbox subtle differences, form styling |
| Samsung Internet | ~3% | Medium - Android default | Older WebKit version, PWA support |
| Opera | ~2% | Low - Chromium-based | Similar to Chrome |
| IE 11 EOL | <1% | Legacy only if required | No modern JS, CSS Grid, Flexbox bugs |
Recommended Testing Stack: Local development: Chrome DevTools + Lighthouse. Validation:
html-validate + Prettier. Accessibility: axe DevTools + manual screen reader testing. Performance: Lighthouse CI
+ WebPageTest. Cross-browser: Playwright for automation + BrowserStack for manual testing. Monitor production
with Google Analytics + Core Web Vitals reporting.