MCPInstall

ux-principles

User experience design principles for developers

Installs
63
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/miles990/claude-software-skills --skill ux-principles
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads ux-principles automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

UX Principles

Overview

Essential UX principles that every developer should know. Good UX isn't just design—it's built into code, architecture, and technical decisions.


Nielsen's 10 Usability Heuristics

1. Visibility of System Status

// ❌ No feedback
async function saveDocument() {
  await api.save(document);
}

// ✅ Clear feedback
async function saveDocument() {
  setStatus('saving');
  try {
    await api.save(document);
    setStatus('saved');
    showToast('Document saved');
  } catch (error) {
    setStatus('error');
    showToast('Failed to save. Please try again.');
  }
}
<!-- Progress indicators -->
<button disabled={isLoading}>
  {isLoading ? (
    <>
      <Spinner /> Saving...
    </>
  ) : (
    'Save'
  )}
</button>

<!-- Upload progress -->
<progress value={uploadProgress} max="100" />
<span>{uploadProgress}% uploaded</span>

2. Match Between System and Real World

// ❌ Technical jargon
"Error: ECONNREFUSED 127.0.0.1:5432"

// ✅ Human language
"We couldn't connect to the database. Please check your internet connection."

// ❌ Developer terms
"Record not found in users table"

// ✅ User terms
"We couldn't find an account with that email address"

3. User Control and Freedom

// Undo functionality
function deleteItem(id: string) {
  const item = items.find(i => i.id === id);
  setItems(items.filter(i => i.id !== id));

  showToast({
    message: 'Item deleted',
    action: {
      label: 'Undo',
      onClick: () => setItems([...items, item])
    },
    duration: 5000
  });
}

// Cancel long operations
const controller = new AbortController();

async function uploadFile(file: File) {
  try {
    await fetch('/upload', {
      method: 'POST',
      body: file,
      signal: controller.signal
    });
  } catch (e) {
    if (e.name === 'AbortError') {
      showToast('Upload cancelled');
    }
  }
}

// User can cancel
<button onClick={() => controller.abort()}>Cancel Upload</button>

4. Consistency and Standards

// Design tokens for consistency
const theme = {
  colors: {
    primary: '#007bff',
    danger: '#dc3545',
    success: '#28a745',
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px',
  },
  borderRadius: {
    sm: '4px',
    md: '8px',
    lg: '16px',
  }
};

// Consistent button patterns
<Button variant="primary">Save</Button>      // Main action
<Button variant="secondary">Cancel</Button>  // Secondary action
<Button variant="danger">Delete</Button>     // Destructive action

5. Error Prevention

// Confirm destructive actions
function deleteAccount() {
  const confirmed = await confirm({
    title: 'Delete Account?',
    message: 'This action cannot be undone. All your data will be permanently deleted.',
    confirmText: 'Delete Account',
    confirmVariant: 'danger'
  });

  if (confirmed) {
    await api.deleteAccount();
  }
}

// Input constraints
<input
  type="number"
  min={0}
  max={100}
  step={1}
  inputMode="numeric"
/>

// Disable invalid actions
<button
  disabled={!isFormValid || isSubmitting}
  title={!isFormValid ? 'Please fill all required fields' : undefined}
>
  Submit
</button>

Accessibility (WCAG)

Semantic HTML

<!-- ❌ Div soup -->
<div class="nav">
  <div class="nav-item" onclick="navigate()">Home</div>
</div>

<!-- ✅ Semantic HTML -->
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/">Home</a></li>
  </ul>
</nav>

<!-- ❌ Missing labels -->
<input type="text" placeholder="Email">

<!-- ✅ Proper labeling -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" required>

ARIA Attributes

<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true">
  {statusMessage}
</div>

<!-- Modal dialogs -->
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-description"
>
  <h2 id="dialog-title">Confirm Action</h2>
  <p id="dialog-description">Are you sure you want to proceed?</p>
</div>

<!-- Loading states -->
<button aria-busy={isLoading} aria-disabled={isLoading}>
  {isLoading ? 'Loading...' : 'Submit'}
</button>

<!-- Expandable sections -->
<button
  aria-expanded={isOpen}
  aria-controls="panel-content"
>
  Show Details
</button>
<div id="panel-content" hidden={!isOpen}>
  Details here...
</div>

Keyboard Navigation

// Focus management
function openModal() {
  setIsOpen(true);
  // Focus first focusable element
  setTimeout(() => {
    modalRef.current?.querySelector('button, [href], input')?.focus();
  }, 0);
}

function closeModal() {
  setIsOpen(false);
  // Return focus to trigger
  triggerRef.current?.focus();
}

// Focus trap in modals
function handleKeyDown(e: KeyboardEvent) {
  if (e.key === 'Tab') {
    const focusable = modalRef.current?.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );

    const first = focusable?.[0];
    const last = focusable?.[focusable.length - 1];

    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last?.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first?.focus();
    }
  }

  if (e.key === 'Escape') {
    closeModal();
  }
}

Color and Contrast

/* WCAG AA: 4.5:1 for normal text, 3:1 for large text */
:root {
  --text-primary: #1a1a1a;     /* High contrast on white */
  --text-secondary: #6b7280;   /* 4.5:1 on white */
  --text-on-primary: #ffffff;  /* White on brand color */
}

/* Don't rely on color alone */
.error-message {
  color: #dc3545;
  /* Also include icon */
  &::before {
    content: "⚠ ";
  }
}

/* Focus indicators */
:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

/* Never remove focus styles entirely */
/* ❌ */ :focus { outline: none; }

Responsive Design

Mobile-First Approach

/* Base styles for mobile */
.container {
  padding: 16px;
}

.grid {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 24px;
  }

  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Desktop */
@media (min-width: 1024px) {
  .container {
    padding: 32px;
    max-width: 1200px;
    margin: 0 auto;
  }

  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

Touch Targets

/* Minimum 44x44px touch targets (WCAG) */
.button {
  min-height: 44px;
  min-width: 44px;
  padding: 12px 16px;
}

/* Adequate spacing between interactive elements */
.button-group {
  display: flex;
  gap: 8px;
}

/* Make entire area tappable */
.card-link {
  position: relative;
}

.card-link::after {
  content: '';
  position: absolute;
  inset: 0;
}

Performance as UX

Perceived Performance

// Optimistic updates
function likePost(postId: string) {
  // Update UI immediately
  setLiked(true);
  setLikeCount(prev => prev + 1);

  // Sync with server in background
  api.likePost(postId).catch(() => {
    // Rollback on failure
    setLiked(false);
    setLikeCount(prev => prev - 1);
    showToast('Failed to like post');
  });
}

// Skeleton loading
function PostList() {
  if (isLoading) {
    return (
      <div className="post-list">
        {[1, 2, 3].map(i => (
          <div key={i} className="post-skeleton">
            <div className="skeleton-avatar" />
            <div className="skeleton-text" />
            <div className="skeleton-text short" />
          </div>
        ))}
      </div>
    );
  }

  return <div className="post-list">{posts.map(renderPost)}</div>;
}

Content Prioritization

<!-- Critical content first -->
<head>
  <!-- Preload critical assets -->
  <link rel="preload" href="/fonts/main.woff2" as="font" crossorigin>
  <link rel="preload" href="/hero-image.webp" as="image">

  <!-- Defer non-critical CSS -->
  <link rel="preload" href="/non-critical.css" as="style" onload="this.rel='stylesheet'">
</head>

<!-- Lazy load below-fold images -->
<img src="product.jpg" loading="lazy" alt="Product image">

<!-- Intersection Observer for infinite scroll -->
<div ref={sentinelRef}>
  {hasMore && <Spinner />}
</div>

Forms UX

Input Design

<!-- Clear labels and help text -->
<div class="form-field">
  <label for="password">Password</label>
  <input
    type="password"
    id="password"
    aria-describedby="password-help"
    minlength="8"
  >
  <small id="password-help">At least 8 characters</small>
</div>

<!-- Inline validation -->
<input
  type="email"
  class={hasError ? 'input-error' : ''}
  aria-invalid={hasError}
  aria-describedby={hasError ? 'email-error' : undefined}
>
{hasError && (
  <span id="email-error" class="error-message" role="alert">
    Please enter a valid email address
  </span>
)}

Form Patterns

// Auto-save drafts
const debouncedSave = useMemo(
  () => debounce((data) => saveDraft(data), 1000),
  []
);

useEffect(() => {
  debouncedSave(formData);
}, [formData]);

// Clear error on input
function handleChange(field: string, value: string) {
  setFormData(prev => ({ ...prev, [field]: value }));
  setErrors(prev => ({ ...prev, [field]: undefined }));
}

// Preserve form state on navigation
useBeforeUnload(
  useCallback((e) => {
    if (hasUnsavedChanges) {
      e.preventDefault();
      return 'You have unsaved changes';
    }
  }, [hasUnsavedChanges])
);

Empty States

function EmptyState({ type }: { type: 'search' | 'empty' | 'error' }) {
  const content = {
    search: {
      icon: <SearchIcon />,
      title: 'No results found',
      message: 'Try adjusting your search or filters',
      action: <Button onClick={clearFilters}>Clear filters</Button>
    },
    empty: {
      icon: <FolderIcon />,
      title: 'No projects yet',
      message: 'Create your first project to get started',
      action: <Button onClick={createProject}>Create Project</Button>
    },
    error: {
      icon: <AlertIcon />,
      title: 'Something went wrong',
      message: 'We couldn\'t load the data. Please try again.',
      action: <Button onClick={retry}>Retry</Button>
    }
  }[type];

  return (
    <div className="empty-state">
      {content.icon}
      <h3>{content.title}</h3>
      <p>{content.message}</p>
      {content.action}
    </div>
  );
}

Related Skills

  • [[frontend]] - UI implementation
  • [[design-patterns]] - UI patterns
  • [[accessibility]] - Detailed WCAG compliance

FAQ

What does the ux-principles skill do?

User experience design principles for developers

How do I install ux-principles?

Run npx skills add https://github.com/miles990/claude-software-skills --skill ux-principles in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/ux-principles/ (personal) or .claude/skills/ux-principles/ (project).

Which AI agents can use ux-principles?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is ux-principles safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains ux-principles?

It is published by miles990 in miles990/claude-software-skills. It has 63 recorded installs.

More from miles990/claude-software-skills

View all →

code-quality

miles990/claude-software-skills

Clean code principles, SOLID, and code review practices

devops-cicd

miles990/claude-software-skills

CI/CD pipelines, infrastructure as code, and deployment strategies

DevOps & Cloud606 installs

game-development

miles990/claude-software-skills

Game development patterns, architectures, and best practices

Media & Creative411 installs

project-management

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

389 installs

e-commerce

miles990/claude-software-skills

E-commerce platforms, payment processing, and shopping cart patterns

Backend & APIs376 installs

saas-platforms

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

362 installs

Popular frontend & design skills

View category →

setup-matt-pocock-skills

mattpocock/skills

Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.

Frontend & Design1.1M installs

frontend-design

anthropics/skills

Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.

OfficialFrontend & Design1M installs

hyperframes-animation

heygen-com/hyperframes

All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.

prototype

mattpocock/skills

Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.

Frontend & Design999.2K installs

hyperframes-core

heygen-com/hyperframes

The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.

Frontend & Design930.5K installs

vercel-react-best-practices

vercel-labs/agent-skills

React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.

OfficialFrontend & Design843.1K installs