Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问clear审计通过

ux-principles用户体验原则

Agent Skill

用于辅助界面设计、视觉规范、排版、配色、布局和交互体验优化。它适合让 Agent 根据产品场景整理页面结构、生成 UI 方案、检查视觉一致性或改进组件层级。使用时需要结合现有品牌、设计系统和用户任务,不应只堆装饰元素;涉及真实页面改动时,应通过截图或浏览器预览检查文本溢出、对齐和响应式表现。

总安装

749

周安装

30

GitHub Stars

12

下载量

242
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:ux-principles(用户体验原则)
来源仓库:https://github.com/miles990/claude-software-skills
仓库路径:skills/ux-principles
安装命令:
npx skills add https://github.com/miles990/claude-software-skills --skill ux-principles
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

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

简介

用于建立一致性的设计准则与判断标准。

  • 适合指导复杂场景下的权衡取舍决策。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。
  • 使用时需结合具体产品类型与目标人群。
  • 帮助团队对齐认知并减少主观争议。ux-principles 属于研究检索类 Skill,可作为该场景下的辅助能力补充。
  • 安装方式:github,可作为内部知识资产沉淀。

SKILL.md

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

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Gemini CLI

26.18%
按下载量换算63

Antigravity

20.87%
按下载量换算51

Claude Code

18.6%
按下载量换算45

windsurf

13.03%
按下载量换算32

OpenCode

8.17%
按下载量换算20

Codex

3.82%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills