@wireweave/ux-rules
v1.4.0
Published
UX validation rules for wireweave
Maintainers
Readme
@wireweave/ux-rules
UX validation rules for Wireweave DSL. This package provides automated UX best practice validation for wireframe designs.
Installation
npm install @wireweave/ux-rules
# or
pnpm add @wireweave/ux-rules
# or
yarn add @wireweave/ux-rulesUsage
import { parse } from '@wireweave/core';
import { validateUX, getUXScore, getUXIssues } from '@wireweave/ux-rules';
const doc = parse(`
page {
card {
input placeholder="Email"
button "Submit" primary
}
}
`);
// Full validation
const result = validateUX(doc);
console.log(result.score); // 0-100, complexity-normalized
console.log(result.rawScore); // 0-100, undamped total penalty
console.log(result.complexity); // { score: 1-10, level: 'simple' | ... }
console.log(result.valid); // true if no errors
console.log(result.issues); // Array of UX issues
// Quick helpers
const score = getUXScore(doc);
const issues = getUXIssues(doc);Rule Categories
| Category | Rules | Description |
|----------|-------|-------------|
| accessibility | 5 | Screen reader support, labels, alt text |
| form | 4 | Form UX patterns, input types, validation |
| usability | 10 | General usability (empty containers, CTA, loading states, cognitive load) |
| touch-target | 5 | Touch target sizes for mobile (WCAG 2.5.5) |
| navigation | 5 | Navigation patterns (item count, active states) |
| consistency | 4 | Consistent styling across components |
| content | 6 | Text quality, placeholder detection, title requirements |
| data-display | 6 | Tables, lists, empty states, pagination |
| feedback | 6 | Spinners, progress bars, toasts, alerts |
| interaction | 8 | Interactive elements need actions defined |
Total: 59 rules
API
validateUX(ast, options?)
Full validation with score and issues.
Options:
categories: Filter by rule categoriesminSeverity: Minimum severity to report ('error'|'warning'|'info')maxIssues: Maximum issues to collectcustomRules: Add custom rulesdisabledRules: Disable rules by ID
Returns UXValidationResult:
valid:trueif no errorsscore: complexity-normalized score (0-100) — see ScoringrawScore: undamped total-penalty score (0-100)complexity:{ score: 1-10, level }from coreanalyze()issues: array ofUXIssuesummary: per-category pass/fail/warning countsseverityCounts:{ errors, warnings, info }
isUXValid(ast)
Quick check - returns true if no errors.
getUXScore(ast)
Returns UX score from 0 to 100.
getUXIssues(ast, options?)
Returns array of UX issues.
formatUXResult(result)
Formats validation result as human-readable string.
Issue Severity
| Severity | Impact | Base Penalty |
|----------|--------|--------------|
| error | Critical UX problem | -10 (absolute) |
| warning | Should fix | -3 (soft) |
| info | Consider improving | -1 (soft) |
Scoring
validateUX() returns two scores:
rawScore— the undamped total penalty, clamped to[0, 100]:100 − (errors × 10 + warnings × 3 + info × 1).score— the headline score. Errors stay an absolute penalty (a hard defect is a hard defect at any screen size — no volume discount). The soft penalty (warnings + info) is damped by the document's achieved complexity:score = round(100 − errors × 10 − (warnings × 3 + info × 1) × dampingFactor) dampingFactor = REFERENCE_COMPLEXITY / max(REFERENCE_COMPLEXITY, complexity.score)complexity.score∈[1, 10]comes from@wireweave/core'sanalyze(), andREFERENCE_COMPLEXITYis3(core's "moderate" band). At or below the reference the factor is1, so a trivial screen earns no relief; above it, a dense, intent-fitting design gets proportional relief for the per-element warning/info heuristics it is expected to accumulate.score >= rawScorealways holds. BothcomplexityandREFERENCE_COMPLEXITYare exported.
All Rules
| Rule ID | Severity | Description |
|---------|----------|-------------|
| a11y-input-label | error/warning | Input fields must have labels |
| a11y-image-alt | warning | Images should have alt text |
| a11y-icon-button-label | error | Icon-only buttons need accessible text |
| a11y-link-text | warning | Links should have descriptive text |
| a11y-heading-hierarchy | warning | Heading levels should be sequential |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| form-submit-button | warning | Forms with inputs need submit button |
| form-required-indicator | info | Required fields should be marked |
| form-password-confirm | info | Registration forms may need password confirmation |
| form-input-type | warning | Use appropriate input types (email, tel, etc.) |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| usability-empty-container | warning | Avoid empty containers |
| usability-clear-cta | info | Pages should have primary button (CTA) |
| usability-loading-states | info | Async buttons may need loading state |
| usability-destructive-confirm | warning | Destructive actions need danger styling |
| usability-modal-close | warning | Modals need close mechanism |
| usability-nesting-depth | warning | Avoid excessive nesting (max 6 levels) |
| usability-too-many-buttons | warning | Max 5 buttons per container |
| usability-too-many-form-fields | info | Max 10 form fields |
| usability-page-complexity | info | Max 50 elements per page |
| usability-drawer-width | info | Drawers should have width |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| touch-button-size | warning | Button minimum 44px (WCAG AAA) |
| touch-icon-button-size | warning | Icon buttons need adequate padding |
| touch-checkbox-radio-size | info | Checkbox/Radio should have labels |
| touch-link-spacing | info | Adjacent links need spacing |
| touch-avatar-size | info | Clickable avatars should be 44px+ |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| nav-item-count | warning | Max 7 navigation items (Miller's Law) |
| nav-active-state | info | Navigation should show active state |
| nav-breadcrumb-home | info | Breadcrumb should start with home |
| nav-tab-count | warning | Max 5 tabs |
| nav-dropdown-items | warning | Dropdown must have items |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| consistency-button-styles | info | Consistent button styles in container |
| consistency-spacing | info | Consistent gap values |
| consistency-card-styling | info | Sibling cards should match |
| consistency-alert-variants | warning | Alert variant should match content |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| content-empty-text | warning | Avoid empty/placeholder text |
| content-button-text-length | info | Button text max 25 characters |
| content-title-length | info | Title max 60 characters |
| content-page-title | warning | Pages should have title |
| content-link-text | error | Links must have text |
| content-no-placeholder | warning | Replace placeholder content |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| data-table-header | warning | Tables need headers |
| data-list-empty-state | info | Lists should have empty state |
| data-table-empty-state | info | Tables should have empty state |
| data-list-pagination | info | Long lists (20+) need pagination |
| data-table-columns | warning | Max 8 table columns |
| data-card-grid | info | Card grids should be consistent |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| feedback-spinner-context | info | Spinners should have context text |
| feedback-progress-value | info | Progress should show value |
| feedback-toast-duration | info | Toast duration 2-10 seconds |
| feedback-alert-dismissible | info | Non-critical alerts should be dismissible |
| feedback-tooltip-length | info | Tooltip max 100 characters |
| feedback-form-errors | info | Forms should handle errors |
| Rule ID | Severity | Description |
|---------|----------|-------------|
| interaction-button-action | warning | Buttons need onClick/action/href |
| interaction-link-destination | warning | Links need href/to destination |
| interaction-form-submit | warning | Forms need onSubmit/action |
| interaction-tab-target | warning | Tabs need target/value |
| interaction-menu-action | warning | Menu items need action |
| interaction-card-action | info | Clickable cards need action |
| interaction-icon-button-action | warning | Icon buttons need action |
| interaction-modal-close | info | Modals need close mechanism |
Custom Rules
Create custom rules using the exported types and utilities:
import { validateUX, UXRule, getNodeText, containsAnyWord } from '@wireweave/ux-rules';
const myRule: UXRule = {
id: 'custom-no-click-here',
category: 'accessibility',
severity: 'warning',
name: 'Avoid "click here" text',
description: 'Link text should be descriptive',
appliesTo: ['Button', 'Link'],
check: (node, context) => {
const text = getNodeText(node).toLowerCase();
if (containsAnyWord(text, ['click here', 'click me'])) {
return {
ruleId: 'custom-no-click-here',
category: 'accessibility',
severity: 'warning',
message: 'Avoid generic "click here" text',
description: 'Use descriptive text that indicates the action',
suggestion: 'Replace with descriptive action text',
path: context.path,
nodeType: node.type,
};
}
return null;
},
};
const result = validateUX(doc, { customRules: [myRule] });Related Packages
- @wireweave/core - Core parser and renderer
License
MIT
