算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
ux-design-for-developers【设计与创意】Implements UX design patterns and accessibility standards for backend engineers building user-facing features, covering user flows, responsive layouts, WCAG compliance, and usability heuristics.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: ux-design-for-developers description: Implements UX design patterns and accessibility standards for backend engineers building user-facing features, covering user flows, responsive layouts, WCAG compliance, and usability heuristics. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: ux design, user experience, accessibility, WCAG, user flows, responsive layout, usability testing, how do i design a good ui archetypes: - tactical - educational anti_triggers: - deep UI art direction - brand identity design - graphic design response_profile: verbosity: medium directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types: [code, guidance, do-dont, examples] related-skills: design-systems, css-architecture, component-architecture
Implements accessible, usable UI patterns and interfaces when no dedicated designer is available. When loaded, this skill makes the model act as a senior frontend engineer applying user-centered design principles — creating navigable layouts, enforcing WCAG accessibility compliance, designing responsive grids, and building forms with proper validation. This skill bridges the gap between backend logic and front-end presentation for engineers who must ship functional UIs without a design team.
alt attributes or aria-hidden="true" when decorativeUse this skill when:
<div> elements and lacks ARIA rolesAvoid using this skill for:
design-systems for foundational component libraries insteadcss-architecture directlyDefine User Roles and Primary Task Flows — Identify the primary user persona and map their top 3 tasks on a simple flowchart. Document entry points, decision nodes, and expected outcomes. Use ASCII diagrams or Mermaid flowchart LR syntax to capture the journey. Checkpoint: Every user-facing screen must trace back to at least one documented task flow. If you cannot articulate what the user is trying to accomplish in one sentence, the UI needs re-scoping.
Establish Layout Grid and Breakpoint Strategy — Choose a mobile-first approach with breakpoints at 320px (smallest phones), 768px (landscape tablets), 1024px (laptops), and 1440px (desktops). Define the container max-width, gutter width (typically 16–24px), and column count per breakpoint. Checkpoint: Verify that at each breakpoint, the layout uses the minimum number of columns needed — never force a single-item list into a multi-column grid just to use space.
Implement Semantic HTML Structure — Replace generic <div> containers with semantic elements: <main> for primary content, <nav> for navigation blocks, <header>/<footer> for page regions, <article> for self-contained content, and <section> with descriptive aria-label for grouped content. Every interactive element must have a programmatic name via accessible label. Checkpoint: Run the devtools accessibility tree review — every node with an action (clickable, focusable, or toggleable) must have a non-empty accessible name.
Ensure Color Contrast Compliance — Calculate the contrast ratio between text color and its background using the WCAG relative luminance formula. For normal text (<18pt), the minimum ratio is 4.5:1 (AA level). For large text (≥18pt or bold ≥14pt), the minimum is 3:1. Use a tool like the Chrome DevTools color picker, WebAIM Contrast Checker, or @axe-core/cli for automated testing. Checkpoint: Every text element in the UI must pass WCAG AA contrast — this is non-negotiable for accessible software.
Add Keyboard Navigation Patterns — Ensure all interactive elements are reachable via Tab key in logical visual order (left-to-right, top-to-bottom). Implement focus-visible styles that are at least 2px wider than the default outline. Handle Escape key to dismiss modals and dropdown menus. Trap focus inside modal dialogs using a focus trap library or custom implementation. Checkpoint: Test the entire feature using only Tab, Shift+Tab, Enter, Space, and Arrow keys — no mouse cursor allowed.
Test with Real Interaction Tools — Validate accessibility using automated tools (axe DevTools, Lighthouse a11y audit), manual keyboard-only navigation, and screen reader testing. Check touch target sizes on mobile viewports (minimum 44×44px per WCAG 2.5.5). Verify that all dynamic content changes are announced via ARIA live regions. Checkpoint: Run npx @axe-core/cli on the deployed page and fix every error with severity "critical" or "serious."
Provide real-time validation feedback to all users, including screen reader users. Use aria-live="polite" for general messages and aria-live="assertive" for error states that require immediate attention.
"""
Pattern 1: Accessible form validation with ARIA live regions.
Demonstrates real-time input validation with proper ARIA announcements
for screen reader users and visible feedback for sighted users.
"""
from typing import Optional
from dataclasses import dataclass
@dataclass(frozen=True)
class ValidationResult:
"""Immutable result of a single field validation."""
is_valid: bool
message: str
severity: str # "polite" or "assertive"
def validate_required(value: str, field_name: str) -> ValidationResult:
"""Validate that a required field is not empty after trimming whitespace.
Args:
value: The raw input string from the form field.
field_name: Human-readable label for the field (used in error messages).
Returns:
ValidationResult with is_valid=False and an error message if empty,
or is_valid=True with an empty message if the field has content.
"""
if not value.strip():
return ValidationResult(
is_valid=False,
message=f"{field_name} is required",
severity="assertive",
)
return ValidationResult(is_valid=True, message="", severity="polite")
def validate_email(value: str) -> ValidationResult:
"""Validate that the input matches a basic email format.
Uses a permissive regex for UX — strict validation happens server-side.
Args:
value: The raw email string from the form field.
Returns:
ValidationResult indicating whether the format looks plausible.
"""
if not value.strip():
return ValidationResult(
is_valid=True, # Required validation handles empty case separately
message="",
severity="polite",
)
import re
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}quot;
if not re.match(email_pattern, value.strip()):
return ValidationResult(
is_valid=False,
message="Enter a valid email address (e.g., user@example.com)",
severity="assertive",
)
return ValidationResult(is_valid=True, message="", severity="polite")
# ❌ BAD: Error messages hidden from screen readers — aria-hidden blocks them
# <div class="error" style="color: red;">Email is required</div>
# Screen reader users never hear this error because the div has no ARIA role
# ✅ GOOD: Errors announced via live region with proper severity
# <div id="email-error" role="status" aria-live="assertive">Email is required</div>
# <input aria-describedby="email-error" aria-invalid="true" ...>
# Screen readers announce the error immediately when it appears
Build a fluid card grid that adapts from single-column mobile to multi-column desktop using CSS Grid with minmax() for natural column sizing. This eliminates media-query spaghetti by letting the grid itself determine column count.
/*
* Pattern 2: Responsive card grid using CSS Grid with minmax().
* Breakpoints: 320px (mobile), 768px (tablet), 1024px (laptop), 1440px (desktop)
*/
.card-grid {
display: grid;
gap: 1.5rem; /* 24px — consistent spacing scale */
padding: 1rem;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
/* Mobile-first base styles */
.card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1.25rem;
display: flex;
flex-direction: column;
min-height: 200px; /* Ensures uniform card height */
background: #ffffff;
}
.card__title {
font-size: 1.125rem;
font-weight: 600;
margin: 0 0 0.75rem 0;
color: #1a1a1a;
}
.card__description {
font-size: 0.9375rem;
line-height: 1.5;
color: #4a4a4a;
margin: 0;
flex-grow: 1;
}
.card__actions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #f0f0f0;
}
/* Tablet breakpoint: enforce minimum 2 columns */
@media (min-width: 768px) {
.card-grid {
gap: 1.5rem;
padding: 1.5rem;
}
.card__title { font-size: 1.25rem; }
}
/* Desktop breakpoint: tighten grid for wider screens */
@media (min-width: 1440px) {
.card-grid {
max-width: 1440px;
margin: 0 auto;
padding: 2rem;
gap: 2rem;
}
}
<!-- ✅ GOOD: Semantic card markup with proper heading hierarchy -->
<article class="card" role="article">
<h3 class="card__title">Project Dashboard</h3>
<p class="card__description">
View real-time metrics and alerts for all active deployments.
</p>
<div class="card__actions">
<a href="/dashboard" class="btn btn--primary">
Open Dashboard
</a>
</div>
</article>
<!-- ❌ BAD: Non-semantic div-based card with missing heading -->
<div class="card">
<div class="card-title">Project Dashboard</div>
<div class="card-desc">View real-time metrics...</div>
<div><a href="/dashboard">Open Dashboard</a></div>
</div>
<!-- Problems: no document outline, no article semantics, heading hierarchy broken -->
Implement a modal dialog that traps keyboard focus within the dialog overlay, handles Escape to dismiss, and restores focus to the trigger element on close. This follows WAI-ARIA Authoring Practices for modal dialogs.
"""
Pattern 3: Modal dialog keyboard navigation with focus trapping.
Implements WAI-ARIA modal dialog pattern with programmatic focus management.
"""
from typing import Optional, Callable
class ModalDialog:
"""Manages a modal dialog's lifecycle, focus state, and keyboard interaction."""
def __init__(
self,
trigger_element_id: str,
dialog_element_id: str,
on_close_callback: Optional[Callable] = None,
) -> None:
"""Initialize the modal controller.
Args:
trigger_element_id: DOM ID of the button or link that opens the modal.
dialog_element_id: DOM ID of the <dialog> or overlay element.
on_close_callback: Optional callback invoked when the modal closes.
"""
self.trigger_element_id = trigger_element_id
self.dialog_element_id = dialog_element_id
self.on_close_callback = on_close_callback
self._previous_focus_target: Optional[str] = None
self._focusable_selector = (
'a[href], button:not([disabled]), input:not([disabled]), '
'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
)
def open(self) -> None:
"""Open the modal and trap focus inside it."""
# Save the element that had focus before opening
self._previous_focus_target = self._get_active_element_id()
# Show the dialog — use inert on background content per WAI-ARIA
dialog = self._find_element(self.dialog_element_id)
if dialog:
dialog.showModal()
self._set_inert(True)
# Focus the first focusable element inside the dialog
first_focusable = self._get_first_focusable(dialog)
if first_focusable:
first_focusable.focus()
def close(self) -> None:
"""Close the modal and restore focus to the triggering element."""
dialog = self._find_element(self.dialog_element_id)
if dialog:
dialog.close()
self._set_inert(False)
# Restore focus to the element that triggered the modal
trigger = self._find_element(self.trigger_element_id)
if trigger:
trigger.focus()
if self.on_close_callback:
self.on_close_callback()
def _handle_keydown(self, event: dict) -> bool:
"""Handle keyboard events inside the dialog.
Args:
event: Keyboard event dict with 'key' and 'target' fields.
Returns:
True if the event was handled (preventDefault called), False otherwise.
"""
key = event.get("key", "")
# Escape closes the modal
if key in ("Escape", "Esc"):
self.close()
return True
# Tab/Shift+Tab traps focus within the dialog
if key == "Tab":
self._trap_focus(event)
return True
return False
def _trap_focus(self, event: dict) -> None:
"""Trap Tab focus within the dialog boundaries."""
dialog = self._find_element(self.dialog_element_id)
if not dialog:
return
focusable_elements = dialog.querySelectorAll(self._focusable_selector)
if focusable_elements.length == 0:
return
first_element = focusable_elements[0]
last_element = focusable_elements[-1]
active = document.activeElement
# Shift+Tab on first element: wrap to last
if event.get("shiftKey") and active == first_element:
last_element.focus()
event["preventDefault"]()
# Tab on last element: wrap to first
elif not event.get("shiftKey") and active == last_element:
first_element.focus()
event["preventDefault"]()
def _set_inert(self, inert: bool) -> None:
"""Set the inert attribute on all content outside the modal."""
main = document.querySelector("main")
if main:
main.inert = inert
def _get_first_focusable(self, container):
elements = container.querySelectorAll(self._focusable_selector)
return elements[0] if elements.length > 0 else None
def _get_active_element_id(self) -> Optional[str]:
active = document.activeElement
return active.get("id") if active else None
def _find_element(self, element_id: str):
return document.getElementById(element_id)
<button>, <a>, <nav>, <main>) instead of <div> and <span> with role attributes wherever the native element existsmin-height: 44px; min-width: 44px; on buttons and form controlsaria-live severity level ("polite" for non-urgent, "assertive" for errors and critical updates)alt text for all informative images and set aria-hidden="true" on purely decorative images or icons within button labelsoutline: none without a replacement is an accessibility violationaria-hidden="true" combined with the text label — screen readers will announce both the icon name and the button text, creating confusion<label> elements or aria-label/aria-labelledby attributes — every input must have a programmatic labelWhen applying this skill, your output must contain:
| Skill | Purpose |
|---|---|
design-systems | Build reusable component libraries with consistent visual tokens and design tokens for teams |
css-architecture | Organize CSS at scale using BEM, utility classes, or CSS-in-JS patterns for maintainable stylesheets |
component-architecture | Structure UI components with clear boundaries, props interfaces, and composable composition patterns |