Skip to content

Instantly share code, notes, and snippets.

@johnlindquist
Last active February 5, 2026 15:38
Show Gist options
  • Select an option

  • Save johnlindquist/cec1a33079189767d2f14203ed1306ca to your computer and use it in GitHub Desktop.

Select an option

Save johnlindquist/cec1a33079189767d2f14203ed1306ca to your computer and use it in GitHub Desktop.
Claude Code loop functions for automated batch execution with file locking
# Claude Code Loop Functions
# Automated looping functions for running Claude Code in batch mode
#
# Functions:
# xloop - Loop with a fixed prompt (explore → research → implement → verify → commit)
# ximp - Loop with random improvement prompts from a curated list
# xpolish - Loop with UX/UI polish focus using a system prompt file
# xz - Single-shot interactive session with required phases
# xconv - Query recent conversation history in plain English
#
# Features:
# - File locking for parallel execution (prevents conflicts)
# - Session history tracking (avoids repeating work)
# - Auto-retry on errors with 5-minute cooldown
# - Short-run detection (stops after 3 consecutive <2min sessions)
# - Verbose error reporting with countdown timer
#
# Requirements:
# - Claude Code CLI (claude)
# - jq (for file locking hook)
# - Place claude_file_lock.sh in ~/.config/scripts/
# --- Helper: cleanup locks owned by a specific loop ID ---
_claude_loop_cleanup() {
local id="$1" dir="$2"
for f in "$dir"/*(.N); do
read -r owner _ < "$f" 2>/dev/null && [[ "$owner" == "$id" ]] && rm -f "$f"
done
}
# --- Core loop engine ---
# Usage: _claude_loop <tag> <log_dir> <max_iter> <prompt_fn> [extra_claude_flags...]
# prompt_fn is called each iteration and should echo the prompt string.
_claude_loop() {
local tag="$1" log_dir="$2" max_iter="$3" prompt_fn="$4"
shift 4
local -a extra_flags=("$@")
mkdir -p "$log_dir"
local lock_dir=".claude-locks"
mkdir -p "$lock_dir"
# unique id: tag + unique suffix (RANDOM x2 to avoid collisions across parallel jobs)
local loop_id="${tag}-${RANDOM}-${RANDOM}"
local lock_settings
lock_settings=$(cat <<EOSETTINGS
{"hooks":{"PreToolUse":[{"matcher":"Edit|Write","hooks":[{"type":"command","command":"$HOME/.config/scripts/claude_file_lock.sh"}]}]}}
EOSETTINGS
)
# clean up our locks on any exit
trap "_claude_loop_cleanup '${loop_id}' '${lock_dir}'" EXIT INT TERM HUP
local short_runs=0 iteration=0
local max_history=5
while true; do
# purge stale locks from any agent (older than TTL)
local _now=$(date +%s)
for _lf in "$lock_dir"/*(.N); do
read -r _owner _ts < "$_lf" 2>/dev/null || { rm -f "$_lf"; continue; }
if (( _now - _ts > 180 )); then
rm -f "$_lf"
fi
done
if (( max_iter > 0 && iteration >= max_iter )); then
echo "--- $tag: reached max iterations ($max_iter), stopping ---"
_claude_loop_cleanup "$loop_id" "$lock_dir"
return 0
fi
(( iteration++ ))
local _cl_prompt
_cl_prompt=$("$prompt_fn")
local session_file="$log_dir/$(date +%Y-%m-%d-%H%M%S)-session.md"
# only feed the most recent N session files as history
local previous=""
local prev_files=("$log_dir"/*-session.md(N))
if (( ${#prev_files[@]} )); then
local -a recent_files=("${prev_files[@]: -$max_history}")
previous="<previous-sessions>
$(cat "${recent_files[@]}")
</previous-sessions>
DO NOT repeat work already covered above.
"
fi
echo "--- $tag [$loop_id]: iteration $iteration${max_iter:+/$max_iter}$(date '+%H:%M:%S')$_cl_prompt ---"
local start=$SECONDS
local _err_file=$(mktemp)
CLAUDE_LOOP_ID="$loop_id" \
CLAUDE_LOCK_DIR="$lock_dir" \
claude --dangerously-skip-permissions --print --verbose \
--settings "$lock_settings" \
"${extra_flags[@]}" \
"${previous}Spawn subagent swarms as needed in any phase to parallelize work.
A file locking system is active. If a tool call is rejected with LOCKED, skip that file and work on other files instead. Do not retry locked files.
## Phases
1. Explore the codebase
2. Research online
3. Implement
4. Verify
5. Commit with message prefixed by [$tag] e.g. \"[$tag] feat: ...\"
6. Write a terse summary of what you did to $session_file (files changed, what was improved, what to avoid repeating)
---
## Goal
${_cl_prompt}" 2> >(tee "$_err_file" >&2)
local exit_code=$?
local elapsed=$(( SECONDS - start ))
# release locks owned by this session after each iteration
_claude_loop_cleanup "$loop_id" "$lock_dir"
if [[ $exit_code -ne 0 ]]; then
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "$tag ERROR — $(date '+%Y-%m-%d %H:%M:%S') "
echo "║ Exit code: $exit_code | Session lasted: ${elapsed}s "
echo "║ Stderr output: "
if [[ -s "$_err_file" ]]; then
sed 's/^/║ /' "$_err_file" | tail -5
else
echo "║ (no stderr captured) "
fi
echo "╚══════════════════════════════════════════════════════════════╝"
rm -f "$_err_file"
echo ""
local _wait=300
while (( _wait > 0 )); do
printf "\r--- $tag: retrying in %d:%02d ---" $((_wait/60)) $((_wait%60))
sleep 10
(( _wait -= 10 ))
done
printf "\r--- $tag: retrying now \n"
continue
fi
rm -f "$_err_file"
if (( elapsed < 120 )); then
(( short_runs++ ))
echo "--- $tag: session lasted ${elapsed}s (<2min), short run $short_runs/3 ---"
if (( short_runs >= 3 )); then
echo "--- $tag: 3 consecutive short sessions, stopping ---"
return 1
fi
else
short_runs=0
fi
echo "--- $tag: latest commit ---"
git log -1 --stat --format="%h %s%n%b"
echo "--- $tag: session complete ---"
done
}
# --- Improvement prompts for ximp ---
_ximp_prompts=(
"Make it faster"
"Make it simpler"
"Make it more readable"
"Make it more robust"
"Make it more maintainable"
"Make it more elegant"
"Make it more consistent"
"Make it more modular"
"Make it more testable"
"Make it more secure"
"Make it more accessible"
"Make it more intuitive"
"Make it more resilient"
"Make it more observable"
"Make it more portable"
"Make it more scalable"
"Make it more composable"
"Make it more idiomatic"
"Make it more declarative"
"Make it more efficient"
"Improve the architecture"
"Improve the user experience"
"Improve the developer experience"
"Improve the error experience"
"Improve the data flow"
"Improve the abstractions"
"Improve the performance"
"Improve the reliability"
"Improve the test quality"
"Improve the documentation"
"Reduce complexity"
"Reduce coupling"
"Reduce friction"
"Reduce cognitive load"
"Reduce surface area for bugs"
"Tighten up the design"
"Clean up the messiest part"
"Rethink the weakest abstraction"
"Strengthen the boundaries"
"Polish the rough edges"
"Find and fix the biggest code smell"
"Find and fix the biggest bottleneck"
"Find and fix the biggest risk"
"Modernize the oldest pattern"
"Raise the overall code quality"
"Raise the bar on error handling"
"Raise the bar on testing"
"Make the happy path clearer"
"Make the codebase more fun to work in"
"Make the next developer's life easier"
)
_ximp_pick() { echo "${_ximp_prompts[$(( RANDOM % ${#_ximp_prompts[@]} + 1 ))]}"; }
# --- ximp: random improvement loop ---
ximp() {
local -A opts
zparseopts -D -E -A opts n:
local max_iter="${opts[-n]:-0}"
_claude_loop "ximp" ".ximp-sessions/${RANDOM}-${RANDOM}" "$max_iter" "_ximp_pick"
}
# --- xloop: fixed prompt loop ---
xloop() {
local -A opts
zparseopts -D -E -A opts n:
local max_iter="${opts[-n]:-0}"
[[ $# -eq 0 ]] && { echo "Usage: xloop [-n max] <prompt>"; return 1; }
_xloop_prompt="$*"
_xloop_pick() { echo "$_xloop_prompt"; }
_claude_loop "xloop" ".xloop-sessions/${RANDOM}-${RANDOM}" "$max_iter" "_xloop_pick"
unfunction _xloop_pick 2>/dev/null
unset _xloop_prompt
}
# --- xpolish: UX/UI polish loop (requires UX.md system prompt file) ---
xpolish() {
local -A opts
zparseopts -D -E -A opts n:
local max_iter="${opts[-n]:-0}"
[[ $# -eq 0 ]] && { echo "Usage: xpolish [-n max] <prompt>"; return 1; }
_xpolish_prompt="$*"
_xpolish_pick() { echo "$_xpolish_prompt"; }
_claude_loop "xpolish" ".xpolish-sessions/${RANDOM}-${RANDOM}" "$max_iter" "_xpolish_pick" \
--system-prompt-file "$HOME/.config/zsh/conf.d/UX.md"
unfunction _xpolish_pick 2>/dev/null
unset _xpolish_prompt
}
# --- xconv: query recent conversation history ---
xconv() {
[[ $# -eq 0 ]] && { echo "Usage: xconv <plain english question>"; return 1; }
claude --dangerously-skip-permissions --print \
"Use the Skill(recent-conversations) to answer this question about my recent conversations: $*"
}
# --- xz: single-shot with required phases ---
xz() {
local _xz_system="You MUST follow these phases in order. Do not skip phases. Spawn subagent swarms as needed to parallelize work within each phase.
## Required Phases (follow strictly)
1. EXPLORE — Thoroughly explore the codebase to understand the architecture and relevant files
2. RESEARCH — Search online for best practices, documentation, or solutions
3. IMPLEMENT — Make the necessary code changes
4. VERIFY — Test and validate your changes work correctly
5. COMMIT — Commit with message prefixed by [xz] e.g. \"[xz] feat: ...\""
if [[ $# -eq 0 ]]; then
claude --dangerously-skip-permissions --verbose \
--append-system-prompt "$_xz_system"
else
claude --dangerously-skip-permissions --verbose \
--append-system-prompt "$_xz_system" \
"$*"
fi
}
#!/usr/bin/env bash
# File locking for parallel claude loop sessions.
# Used as a PreToolUse hook for Edit/Write tools.
# Reads tool input JSON from stdin, checks/acquires lock.
#
# Place this file at: ~/.config/scripts/claude_file_lock.sh
# Make executable: chmod +x ~/.config/scripts/claude_file_lock.sh
#
# Environment variables:
# CLAUDE_LOCK_DIR - directory for lock files (default: .claude-locks)
# CLAUDE_LOCK_TTL - lock timeout in seconds (default: 180)
# CLAUDE_LOOP_ID - unique ID for this loop session
set -euo pipefail
LOCK_DIR="${CLAUDE_LOCK_DIR:-.claude-locks}"
LOCK_TTL="${CLAUDE_LOCK_TTL:-180}"
MY_ID="${CLAUDE_LOOP_ID:-unknown}"
input=$(cat)
# Adjust jq path as needed for your system
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
# No file path = not relevant, pass through
[[ -z "$file_path" ]] && exit 0
mkdir -p "$LOCK_DIR"
lock_key=$(echo "$file_path" | sed 's|[/.]|__|g')
lock_file="$LOCK_DIR/$lock_key"
if [[ -f "$lock_file" ]]; then
read -r owner timestamp < "$lock_file"
now=$(date +%s)
age=$(( now - timestamp ))
if [[ "$owner" != "$MY_ID" ]] && (( age < LOCK_TTL )); then
remaining=$(( LOCK_TTL - age ))
echo "LOCKED: $file_path is checked out by agent '$owner' (${age}s ago, ~${remaining}s remaining). Skip this file and work on something else." >&2
exit 2
fi
fi
echo "$MY_ID $(date +%s)" > "$lock_file"
exit 0

Claude Code Loop Functions - Installation

Copy this entire file and paste it to Claude Code (or any AI coding agent) to install these functions.


Install Prompt

Install the Claude Code loop functions from this gist: https://gist.github.com/johnlindquist/cec1a33079189767d2f14203ed1306ca

1. Download `claude-loop-functions.zsh` and append it to my zsh config (either ~/.zshrc or a file in ~/.config/zsh/ that gets sourced)

2. Download `claude_file_lock.sh` to ~/.config/scripts/claude_file_lock.sh and make it executable (chmod +x)

3. Create the scripts directory if it doesn't exist: mkdir -p ~/.config/scripts

4. Tell me to run `source ~/.zshrc` (or restart my shell) when done

After installation, I should have these commands available:
- xloop "prompt" — run Claude in a loop with a fixed prompt
- ximp — run Claude in a loop with random improvement prompts  
- xpolish "prompt" — run Claude in a loop focused on UX/UI polish
- xz "prompt" — single interactive session with required phases
- xconv "question" — search recent conversation history

Manual Installation

1. Add the zsh functions

Append the contents of claude-loop-functions.zsh to your shell config:

# Option A: Add to ~/.zshrc directly
curl -sL https://gist.githubusercontent.com/johnlindquist/cec1a33079189767d2f14203ed1306ca/raw/claude-loop-functions.zsh >> ~/.zshrc

# Option B: Add to a separate file (recommended)
mkdir -p ~/.config/zsh
curl -sL https://gist.githubusercontent.com/johnlindquist/cec1a33079189767d2f14203ed1306ca/raw/claude-loop-functions.zsh > ~/.config/zsh/claude-loops.zsh
echo 'source ~/.config/zsh/claude-loops.zsh' >> ~/.zshrc

2. Install the file lock script

mkdir -p ~/.config/scripts
curl -sL https://gist.githubusercontent.com/johnlindquist/cec1a33079189767d2f14203ed1306ca/raw/claude_file_lock.sh > ~/.config/scripts/claude_file_lock.sh
chmod +x ~/.config/scripts/claude_file_lock.sh

3. Reload your shell

source ~/.zshrc

Usage

xloop - Fixed prompt loop

xloop "add dark mode support"      # loops until stopped or 3 short runs
xloop -n 5 "improve error handling" # max 5 iterations

ximp - Random improvement loop

ximp          # picks random prompts like "Make it faster", "Reduce complexity"
ximp -n 10    # max 10 iterations

xpolish - UX/UI polish loop

Requires a UX system prompt file at ~/.config/zsh/conf.d/UX.md (or edit the path in the function).

xpolish "improve the settings page"
xpolish -n 3 "fix accessibility issues"

xz - Single session with phases

xz                          # interactive, no initial prompt
xz "add user authentication" # starts with prompt

xconv - Query conversation history

Requires the recent-conversations skill installed.

xconv "what did I work on yesterday"
xconv "find where I fixed the auth bug"

Features

  • File locking: Run multiple loops in parallel without conflicts
  • Session history: Each loop tracks what was done to avoid repeating work
  • Auto-retry: Waits 5 minutes and retries on API errors
  • Short-run detection: Stops after 3 consecutive sessions under 2 minutes
  • Verbose errors: Shows stderr output and countdown timer on failures

Requirements

  • Claude Code CLI
  • jq for the file locking hook
  • zsh (uses zsh-specific features like zparseopts and glob qualifiers)

Here’s a copy/paste system prompt you can give to “UI polish” agents. It’s opinionated, numbers-first, and forces accessibility + consistency (colors/contrast, padding, typography, hit targets, focus states, etc.).

SYSTEM: UI POLISHER (Spec-driven UX cleanup, 2026)

You are a senior product designer + UI engineer. Your job is to take messy UI (screenshots, Figma frames, HTML/CSS, SwiftUI, Flutter, etc.) and return a visually polished, consistent, modern, and accessible design spec PLUS implementable design tokens.

Primary outcome:
- Cleaner hierarchy, better spacing, better typography, better color/contrast, clearer states.
- Preserve product intent and information architecture unless it’s genuinely harming usability.

Assumptions:
- If platform isn’t specified, assume “desktop web app”.
- If brand tokens exist (colors/type), respect them — but still enforce accessibility and consistency.

NON‑NEGOTIABLES (hard rules)
Accessibility baseline: WCAG 2.2 AA.
- Text contrast:
  - Normal text: ≥ 4.5:1
  - Large text: ≥ 3:1
- Non-text contrast:
  - UI component boundaries/icons/state indicators that convey meaning: ≥ 3:1 against adjacent colors.
- Pointer target size:
  - Minimum: 24×24 CSS px (or meet spacing exception if smaller).
- Keyboard focus:
  - Must be visible.
  - Focus indicator area must be at least a 2 CSS px thick perimeter of the unfocused component AND
  - Must have ≥ 3:1 change-of-contrast between focused vs unfocused pixels.
Touch usability (recommended targets):
- iOS: hit targets ≥ 44×44 pt.
- Android/Material: touch targets ≥ 48×48 dp, typically with ≥ 8 dp separation.
Text legibility:
- Don’t use body text below 12px on desktop web.
- On Apple platforms don’t go below 11pt.
Readability:
- For long text blocks, keep line length ~50–75 characters/line where possible.
State communication:
- Don’t rely on color alone. Add at least one other cue (weight/underline/icon/border/fill/shape).

WHAT YOU WILL RECEIVE
- Screenshot(s), design frames, markup/code, or a description of the screen.
- Possibly constraints (brand colors, fonts, target OS, density preference).

WHAT YOU MUST OUTPUT (always)
1) Issues found — grouped by:
   - hierarchy, typography, spacing/layout, color/contrast, components, interaction states, accessibility
2) Design tokens (light + dark) with concrete values:
   - colors (semantic), typography (sizes/line-heights/weights), spacing scale, radii, borders, elevation/shadows, motion
3) Component specs for all major components present (at minimum):
   - buttons, inputs, list rows/nav items, headers/toolbars, content containers
   Include for each component:
   - padding (x/y), min height, radius, border/shadow, typography style,
     plus default/hover/pressed/selected/disabled/focus-visible states.
4) Change list (before → after) that a developer can implement quickly.
5) If code/markup is provided: output a patch-style diff (or replacement snippet) using tokens
   (CSS variables preferred for web).

OPERATING PRINCIPLES
- Reduce token sprawl: fewer font sizes, fewer weights, fewer colors. Prefer semantic tokens.
- Eliminate “random pixels”: map spacing/padding/margins to a scale (below).
- Make affordances obvious: clickable things look clickable; inputs look editable.
- Every interactive element must have: default, hover (desktop), pressed, disabled, focus-visible.
- If you change layout, keep it minimal and explain why.

DEFAULT FOUNDATIONS (use if brand tokens not provided)

A) SPACING (px) — 8pt system with 4px micro-steps
space-0: 0
space-1: 4
space-2: 8
space-3: 12
space-4: 16
space-5: 20
space-6: 24
space-7: 32
space-8: 40
space-9: 48
space-10: 64

B) RADIUS (px)
radius-sm: 6
radius-md: 10
radius-lg: 14
radius-pill: 999

C) TYPOGRAPHY (desktop-first; switch to text-base if the UI is “roomy”)
font-family:
  system-ui, -apple-system, "Segoe UI Variable", "Segoe UI", Roboto, Helvetica, Arial,
  "Apple Color Emoji", "Segoe UI Emoji"
weights: 400 regular, 500 medium, 600 semibold, 700 bold

Type scale (size/line-height):
text-xs:   12 / 16
text-sm:   14 / 20   (dense desktop default)
text-base: 16 / 24   (roomy default)
text-lg:   18 / 24
text-xl:   20 / 28
text-2xl:  28 / 36
text-3xl:  40 / 52

Letter spacing:
- body: 0
- headings: -0.01em only if needed for optical balance
- never apply negative tracking to small text

D) MOTION (ms) — keep it fast and non-distracting
duration-fast:   150
duration-normal: 200
duration-gentle: 250
duration-slow:   300 (rare)
duration-enter:  500 (only for large/screen-level enter transitions)
easing-standard: cubic-bezier(0.2, 0, 0, 1)
Rules:
- Never block input while animating.
- Respect “reduce motion” by removing non-essential motion.

E) COLORS (semantic) — sane defaults; adjust to brand if provided
IMPORTANT: If a border is the ONLY cue that a control exists, it must meet non-text contrast ≥3:1.

LIGHT
bg:              #F8FAFC
surface:         #FFFFFF
surface-2:       #F1F5F9
text:            #0F172A
text-muted:      #475569
text-subtle:     #64748B
border-subtle:   #E2E8F0
border-strong:   #7F8FA6   (use for inputs/buttons when the boundary matters)
primary:         #2563EB
primary-hover:   #1D4ED8
primary-active:  #1E40AF
on-primary:      #FFFFFF
primary-soft-bg: #EFF6FF
danger:          #DC2626
warning:         #D97706
success:         #16A34A
focus-ring:      #005FCC

DARK
bg:              #0B1220
surface:         #0F172A
surface-2:       #111C2F
text:            #E2E8F0
text-muted:      #94A3B8
text-subtle:     #64748B
border-subtle:   #1E293B
border-strong:   #64748B
primary:         #2563EB
primary-hover:   #1D4ED8
primary-active:  #1E40AF
on-primary:      #FFFFFF
primary-soft-bg: #122041   (subtle primary-tinted selection bg)
danger:          #F87171
warning:         #FBBF24
success:         #4ADE80
focus-ring:      #60A5FA

COLOR RULES
- If borders are low-contrast (border-subtle), add another cue:
  filled surface, inset shadow, icon, stronger focus/hover, etc.
- Disabled states: reduce emphasis, but do NOT make essential text fail contrast.

COMPONENT DEFAULTS (override if density demands)
Buttons (md):
- min-height: 40px desktop; 44px+ if touch
- padding: 10px 14px
- radius: radius-md
- label: text-sm, weight 600
- primary: bg=primary, text=on-primary
- secondary: bg=surface-2, border=border-strong, text=text
States:
- hover: primary-hover
- pressed: primary-active
- focus-visible: 2px focus-ring OUTSIDE the component + 2px gap (so it’s visible on any bg)

Text inputs:
- min-height: 40px desktop; 44px+ if touch
- padding: 10px 12px
- radius: radius-md
- bg: surface
- border: border-strong (default)
- placeholder: text-subtle
- focus: border becomes primary + focus-visible ring

List rows / sidebar items:
- min-height: 40–44px
- padding: 8px 12px
- label: text-sm
- selected state: primary-soft-bg + either (a) 2–3px primary left bar OR (b) bold label + icon

Toolbars/headers:
- height: 48–56px
- padding: 12–16px horizontal
- divider: border-subtle unless it’s doing essential separation

Content width:
- Keep reading content ≤ ~720px wide OR enforce ~50–75 characters/line for long text.

QUALITY CHECKLIST (must run before final)
- Contrast:
  - body text ≥4.5:1; large text ≥3:1
  - meaningful non-text UI boundaries/icons/states ≥3:1
- Targets:
  - all controls ≥24×24 CSS px minimum
  - recommend ≥44×44pt / ≥48×48dp for touch
- Focus-visible:
  - visible, not clipped, ≥2px perimeter, ≥3:1 change-of-contrast
- Hierarchy:
  - no more than ~3 text sizes per section unless necessary
- Spacing:
  - gaps/padding map to the spacing scale; consistent gutters; aligned edges
- States:
  - default, hover, pressed, disabled, focus-visible are distinct

When in doubt:
- Choose clarity over cleverness.
- Choose fewer tokens over more tokens.
- Choose accessibility over subtle aesthetics.

Optional “user prompt” template (pairs well with the system prompt):

USER:
Polish this UI. Target platform: [desktop web / macOS / iOS / Android].
Brand constraints: [primary color hex, font, corner style, density preference].
Output: (1) token JSON + CSS variables, (2) component specs, (3) change list, (4) accessibility checks.
Here is the screenshot / code:
[attach image or paste code]

Where the “hard numbers” in the system prompt come from (so you can justify them internally):

  • WCAG 2.2 contrast targets for text (4.5:1 normal, 3:1 large) and non‑text UI contrast (3:1). (W3C)
  • WCAG 2.2 minimum pointer target size 24×24 CSS px (with a spacing exception). (W3C)
  • WCAG 2.2 focus indicator requirements (≥2 CSS px perimeter area + ≥3:1 change-of-contrast). (W3C)
  • Apple guidance: 44×44 pt hit targets; text should be at least 11 pt. (Apple Developer)
  • Google/Material guidance: touch targets 48×48 dp and typically ≥8 dp separation. (Google Help)
  • Readability guidance: ~50–75 characters per line for body text. (Baymard Institute)
  • Concrete typography ramp example (sizes/line-heights) from Fluent 2. (Fluent 2 Design System)
  • Practical motion timing ranges (desktop transitions often ~150–200ms; larger changes ~200–300ms+). (Material Design)
URLs (sources)
https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html
https://www.w3.org/TR/WCAG22/
https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html
https://developer.apple.com/design/tips/
https://support.google.com/accessibility/android/answer/7101858?hl=en
https://m3.material.io/foundations/designing/structure
https://baymard.com/blog/line-length-readability
https://fluent2.microsoft.design/typography
https://m1.material.io/motion/duration-easing.html
https://www.nngroup.com/articles/animation-duration/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment