{"id":"a3259ad0-77c6-4d12-bcb6-6aea64082506","shortId":"ZtnXXa","kind":"skill","title":"loading-states-and-perceived-performance","tagline":"Manage user expectations during wait times with appropriate loading states — from simple spinners to complex skeleton screens and staggered animations. Perceived performance is often more important than actual load time. Use when designing data-heavy components, handling API call","description":"# Loading States and Perceived Performance\n\nUsers don't mind waiting as much if they understand *what* they are waiting for and *how much* progress is being made. Perceived performance is the design work of making a system feel faster than it actually is.\n\n---\n\n## Choosing the Right Loading State\n\n| Wait Duration | Best Pattern | Use for |\n|---|---|---|\n| **Short (< 1s)** | **Inline Spinner / Loader** | Button actions, small updates, quick data fetches |\n| **Medium (1s – 3s)** | **Skeleton Screen** | Cards, lists, dashboards, profile pages |\n| **Long (> 3s)** | **Determinate Progress Bar** | File uploads, complex exports, heavy processing |\n| **Full Page** | **Staggered Entry / Animated Sections** | Initial app load, hero sections, immersive transitions |\n\n---\n\n## Simple Cases: Spinners and Loaders\n\nUse spinners for small, contained actions where the layout doesn't change significantly.\n\n- **Button Spinners:** Replace button text or sit alongside it. The button should enter a `disabled` state to prevent double-submissions.\n- **Micro-Loaders:** A small 16–24px circle for inline updates (e.g., saving a single field).\n- **Animation Tip:** A \"spring-loaded\" rotation (easing in and out) feels more premium than a constant linear rotation.\n\n```css\n@keyframes spin {\n  0%   { transform: rotate(0deg); }\n  100% { transform: rotate(360deg); }\n}\n.spinner {\n  animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;\n}\n```\n\n---\n\n## Skeleton Screens (Glimmer/Shimmer)\n\nSkeleton screens provide a visual placeholder that mimics the layout of the final content. This reduces \"layout shift\" (CLS) and signals to the user exactly where the content will appear.\n\n### The Shimmer Effect\nA subtle, moving gradient that travels across the skeleton elements.\n\n```css\n.skeleton {\n  background: var(--color-grey-100);\n  background-image: linear-gradient(\n    90deg,\n    rgba(255, 255, 255, 0) 0%,\n    rgba(255, 255, 255, 0.5) 50%,\n    rgba(255, 255, 255, 0) 100%\n  );\n  background-size: 200% 100%;\n  animation: shimmer 1.5s infinite;\n}\n\n@keyframes shimmer {\n  0%   { background-position: -200% 0; }\n  100% { background-position: 200% 0; }\n}\n```\n\n### Rules for Skeletons\n- **Match the shape:** If the final content is a round avatar, use a round skeleton. If it's a 2-line heading, use two bars of varying widths.\n- **Stay Recessive:** Skeletons should use your most subtle grey (`--color-grey-100` or `grey-50`). They should not draw focus.\n- **Fade into Reality:** When data arrives, fade the actual content in over the skeleton (150–200ms) rather than snapping.\n\n---\n\n## Fully Animated Sections\n\nFor major page transitions or initial loads, use a coordinated animation strategy.\n\n### Staggered Entry (Cascading)\nInstead of the whole page appearing at once, animate sections in a sequence. This guides the user's eye from the most important content (hero) down to secondary areas.\n\n```css\n.section {\n  opacity: 0;\n  transform: translateY(10px);\n  animation: slide-up 400ms ease-out forwards;\n}\n/* Stagger by index */\n.section:nth-child(1) { animation-delay: 100ms; }\n.section:nth-child(2) { animation-delay: 200ms; }\n.section:nth-child(3) { animation-delay: 300ms; }\n\n@keyframes slide-up {\n  to { opacity: 1; transform: translateY(0); }\n}\n```\n\n### Hero Section \"Bloom\"\nFor hero sections, you might use a more complex animation:\n1. **Background image** fades in slowly.\n2. **Heading** slides in with a slight overshoot (spring).\n3. **CTA button** appears last with a crisp fade-in or subtle color transition.\n\n---\n\n## Advanced: Optimistic UI\n\nThe fastest UI is one that doesn't wait for the server at all.\n- **The Pattern:** Update the UI immediately assuming the server call will succeed. If it fails, roll back and show an error.\n- **Use for:** Liking a post, toggling a switch, renaming a folder, deleting a message.\n- **Benefit:** Instant gratification for the user, making the app feel \"lightning fast.\"\n\n---\n\n## Adding Delight to the Wait\n\nLoading doesn't have to be a neutral experience. For waits longer than 2 seconds, consider adding brand personality and \"delight\" to keep the user engaged.\n\n### Brand-Aligned Micro-copy\nReplace generic \"Loading...\" text with wording that reflects the brand's voice.\n- **Technical:** \"Compiling data...\", \"Syncing with cloud...\"\n- **Playful:** \"Gathering pixels...\", \"Brewing your dashboard...\", \"Almost there!\"\n- **Professional:** \"Preparing your report...\", \"Verifying details...\"\n\n### Branded Animations (Lottie/SVG)\nFor significant loading moments (initial app boot, complex data processing), replace the standard spinner with a small, brand-specific animation. \n- A designer's tool might show a pencil drawing a line.\n- A fitness app might show a pulsing heart or a moving runner icon.\n- A financial tool might show coins stacking or a chart line moving upward.\n\n### Progressive Storytelling\nIf a wait is consistently long (3s+), use the loading area to tell a small story or provide value:\n- **Tips & Tricks:** \"Did you know you can use Ctrl+K to search?\"\n- **Process Transparency:** Show what the system is doing: \"Checking database...\" → \"Optimising results...\" → \"Finalising view...\"\n\n### Visual Transitions (Arrival)\n\nWhen transitioning from a loading state to content, use a crisp fade-in (150ms) to make the arrival feel like a reward. Avoid scaling the incoming content, as it can cause layout instability.\n\n---\n\n## Review Checklist\n\n- [ ] Is the loading state appropriate for the expected wait duration (spinner vs skeleton)?\n- [ ] Does the skeleton screen match the physical layout of the incoming content?\n- [ ] Is there a subtle shimmer animation on skeletons to signal \"active loading\"?\n- [ ] Are buttons disabled during loading to prevent duplicate actions?\n- [ ] Does content fade in over skeletons (150–200ms) rather than blinking into existence?\n- [ ] For full-page loads, is a staggered entry used to guide the eye?\n- [ ] Is `prefers-reduced-motion` respected for all loading animations?\n- [ ] In \"Optimistic UI\" moments, is there a clear rollback path if the action fails?\n\n## Common Anti-Patterns\n\n| Anti-pattern | Problem | Fix |\n|---|---|---|\n| A global spinner that blocks the whole app | High frustration, user cannot browse other areas | Use contextual loaders or skeletons |\n| Skeletons that don't match the final layout | Massive layout shift (CLS) when data arrives | Match shapes and sizes exactly |\n| Too many spinners on one page | Visual noise, feels like the whole app is broken | Group loading states into a single container skeleton |\n| Faster-than-light skeletons | Shimmer animation that is too fast or high-contrast | Keep shimmer slow (1.5s+) and very subtle |","tags":["loading","states","and","perceived","performance","dembrandt","skills","accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills"],"capabilities":["skill","source-dembrandt","skill-loading-states-and-perceived-performance","topic-accessibility","topic-agent-skills","topic-claude-code-skills","topic-claude-skills","topic-cursor-skills","topic-design-system","topic-design-tokens","topic-enterprise-ux","topic-gestalt","topic-skills-sh","topic-typography","topic-ui-design"],"categories":["dembrandt-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dembrandt/dembrandt-skills/loading-states-and-perceived-performance","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dembrandt/dembrandt-skills","source_repo":"https://github.com/dembrandt/dembrandt-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 9 github stars · SKILL.md body (6,649 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:08:27.410Z","embedding":null,"createdAt":"2026-05-18T13:14:02.361Z","updatedAt":"2026-05-18T19:08:27.410Z","lastSeenAt":"2026-05-18T19:08:27.410Z","tsv":"'-200':339 '-50':393 '0':224,240,309,310,321,335,340,346,468,520 '0.2':241 '0.4':239 '0.5':315 '0deg':227 '1':242,488,517,534 '1.5':330,1031 '100':228,297,322,327,341,390 '100ms':492 '10px':471 '150':413,896 '150ms':822 '16':191 '1s':102,114 '2':369,497,540,646 '200':326,345 '200ms':414,501,897 '24px':192 '255':306,307,308,312,313,314,318,319,320 '3':506,549 '300ms':510 '360deg':231 '3s':115,124,766 '400ms':476 '50':316 '800ms':235 '90deg':304 'across':286 'action':107,157,889,939 'activ':879 'actual':34,88,407 'ad':628,649 'advanc':564 'align':661 'almost':689 'alongsid':172 'anim':26,138,202,233,328,419,431,444,472,490,499,508,533,698,720,874,926,1019 'animation-delay':489,498,507 'anti':943,946 'anti-pattern':942,945 'api':45 'app':141,624,705,734,957,1002 'appear':276,441,552 'appropri':14,848 'area':464,770,964 'arriv':404,807,826,984 'assum':587 'avatar':360 'avoid':831 'back':597 'background':292,299,324,337,343,535 'background-imag':298 'background-posit':336,342 'background-s':323 'bar':127,374 'benefit':616 'best':97 'bezier':238 'blink':900 'block':954 'bloom':523 'boot':706 'brand':650,660,674,697,718 'brand-align':659 'brand-specif':717 'brew':686 'broken':1004 'brows':962 'button':106,165,168,175,551,882 'call':46,590 'cannot':961 'card':118 'cascad':435 'case':148 'caus':839 'chang':163 'chart':754 'check':799 'checklist':843 'child':487,496,505 'choos':90 'circl':193 'clear':934 'cloud':682 'cls':265,981 'coin':750 'color':295,388,562 'color-grey':294,387 'common':941 'compil':678 'complex':21,130,532,707 'compon':43 'consid':648 'consist':764 'constant':218 'contain':156,1011 'content':260,274,356,408,459,815,835,868,891 'contextu':966 'contrast':1027 'coordin':430 'copi':664 'crisp':556,818 'css':221,290,465 'cta':550 'ctrl':787 'cubic':237 'cubic-bezi':236 'dashboard':120,688 'data':41,111,403,679,708,983 'data-heavi':40 'databas':800 'delay':491,500,509 'delet':613 'delight':629,653 'design':39,78,722 'detail':696 'determin':125 'disabl':179,883 'doesn':161,573,634 'doubl':184 'double-submiss':183 'draw':397,729 'duplic':888 'durat':96,853 'e.g':197 'eas':209,478 'ease-out':477 'effect':279 'element':289 'engag':658 'enter':177 'entri':137,434,911 'error':601 'exact':271,989 'exist':902 'expect':9,851 'experi':641 'export':131 'eye':454,916 'fade':399,405,537,558,820,892 'fade-in':557,819 'fail':595,940 'fast':627,1023 'faster':85,1014 'faster-than-light':1013 'fastest':568 'feel':84,213,625,827,998 'fetch':112 'field':201 'file':128 'final':259,355,976 'finalis':803 'financi':746 'fit':733 'fix':949 'focus':398 'folder':612 'forward':480 'frustrat':959 'full':134,905 'full-pag':904 'fulli':418 'gather':684 'generic':666 'glimmer/shimmer':246 'global':951 'gradient':283,303 'gratif':618 'grey':296,386,389,392 'group':1005 'guid':450,914 'handl':44 'head':371,541 'heart':739 'heavi':42,132 'hero':143,460,521,525 'high':958,1026 'high-contrast':1025 'icon':744 'imag':300,536 'immedi':586 'immers':145 'import':32,458 'incom':834,867 'index':483 'infinit':243,332 'initi':140,426,704 'inlin':103,195 'instabl':841 'instant':617 'instead':436 'k':788 'keep':655,1028 'keyfram':222,333,511 'know':783 'last':553 'layout':160,256,263,840,864,977,979 'light':1016 'lightn':626 'like':604,828,999 'line':370,731,755 'linear':219,302 'linear-gradi':301 'list':119 'load':2,15,35,47,93,142,207,427,633,667,702,769,812,846,880,885,907,925,1006 'loader':105,151,188,967 'loading-states-and-perceived-perform':1 'long':123,765 'longer':644 'lottie/svg':699 'made':73 'major':422 'make':81,622,824 'manag':7 'mani':991 'massiv':978 'match':350,861,974,985 'medium':113 'messag':615 'micro':187,663 'micro-copi':662 'micro-load':186 'might':528,725,735,748 'mimic':254 'mind':55 'moment':703,930 'motion':921 'move':282,742,756 'much':58,69 'neutral':640 'nois':997 'nth':486,495,504 'nth-child':485,494,503 'often':30 'one':571,994 'opac':467,516 'optimis':801 'optimist':565,928 'overshoot':547 'page':122,135,423,440,906,995 'path':936 'pattern':98,582,944,947 'pencil':728 'perceiv':5,27,50,74 'perform':6,28,51,75 'person':651 'physic':863 'pixel':685 'placehold':252 'play':683 'posit':338,344 'post':606 'prefer':919 'prefers-reduced-mot':918 'premium':215 'prepar':692 'prevent':182,887 'problem':948 'process':133,709,791 'profession':691 'profil':121 'progress':70,126,758 'provid':249,777 'puls':738 'quick':110 'rather':415,898 'realiti':401 'recess':379 'reduc':262,920 'reflect':672 'renam':610 'replac':167,665,710 'report':694 'respect':922 'result':802 'review':842 'reward':830 'rgba':305,311,317 'right':92 'roll':596 'rollback':935 'rotat':208,220,226,230 'round':359,363 'rule':347 'runner':743 'save':198 'scale':832 'screen':23,117,245,248,860 'search':790 'second':647 'secondari':463 'section':139,144,420,445,466,484,493,502,522,526 'sequenc':448 'server':578,589 'shape':352,986 'shift':264,980 'shimmer':278,329,334,873,1018,1029 'short':101 'show':599,726,736,749,793 'signal':267,878 'signific':164,701 'simpl':18,147 'singl':200,1010 'sit':171 'size':325,988 'skeleton':22,116,244,247,288,291,349,364,380,412,856,859,876,895,969,970,1012,1017 'skill' 'skill-loading-states-and-perceived-performance' 'slide':474,513,542 'slide-up':473,512 'slight':546 'slow':1030 'slowli':539 'small':108,155,190,716,774 'snap':417 'source-dembrandt' 'specif':719 'spin':223,234 'spinner':19,104,149,153,166,232,713,854,952,992 'spring':206,548 'spring-load':205 'stack':751 'stagger':25,136,433,481,910 'standard':712 'state':3,16,48,94,180,813,847,1007 'stay':378 'stori':775 'storytel':759 'strategi':432 'submiss':185 'subtl':281,385,561,872,1035 'succeed':592 'switch':609 'sync':680 'system':83,796 'technic':677 'tell':772 'text':169,668 'time':12,36 'tip':203,779 'toggl':607 'tool':724,747 'topic-accessibility' 'topic-agent-skills' 'topic-claude-code-skills' 'topic-claude-skills' 'topic-cursor-skills' 'topic-design-system' 'topic-design-tokens' 'topic-enterprise-ux' 'topic-gestalt' 'topic-skills-sh' 'topic-typography' 'topic-ui-design' 'transform':225,229,469,518 'transit':146,424,563,806,809 'translatey':470,519 'transpar':792 'travel':285 'trick':780 'two':373 'ui':566,569,585,929 'understand':61 'updat':109,196,583 'upload':129 'upward':757 'use':37,99,152,361,372,382,428,529,602,767,786,816,912,965 'user':8,52,270,452,621,657,960 'valu':778 'var':293 'vari':376 'verifi':695 'view':804 'visual':251,805,996 'voic':676 'vs':855 'wait':11,56,65,95,575,632,643,762,852 'whole':439,956,1001 'width':377 'word':670 'work':79","prices":[{"id":"45fc6e3e-012c-40e9-b496-75f5dc8b4234","listingId":"a3259ad0-77c6-4d12-bcb6-6aea64082506","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dembrandt","category":"dembrandt-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:02.361Z"}],"sources":[{"listingId":"a3259ad0-77c6-4d12-bcb6-6aea64082506","source":"github","sourceId":"dembrandt/dembrandt-skills/loading-states-and-perceived-performance","sourceUrl":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/loading-states-and-perceived-performance","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:02.361Z","lastSeenAt":"2026-05-18T19:08:27.410Z"}],"details":{"listingId":"a3259ad0-77c6-4d12-bcb6-6aea64082506","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dembrandt","slug":"loading-states-and-perceived-performance","github":{"repo":"dembrandt/dembrandt-skills","stars":9,"topics":["accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills","design-system","design-tokens","enterprise-ux","gestalt","skills-sh","typography","ui-design","ux","wcag"],"license":"mit","html_url":"https://github.com/dembrandt/dembrandt-skills","pushed_at":"2026-05-14T22:34:06Z","description":"UX and design system skills for AI agents based on 20 years of experience","skill_md_sha":"c7b03c573fb3348b9cee66be197cf405cad23bc5","skill_md_path":"skills/loading-states-and-perceived-performance/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/loading-states-and-perceived-performance"},"layout":"multi","source":"github","category":"dembrandt-skills","frontmatter":{"name":"loading-states-and-perceived-performance","description":"Manage user expectations during wait times with appropriate loading states — from simple spinners to complex skeleton screens and staggered animations. Perceived performance is often more important than actual load time. Use when designing data-heavy components, handling API calls, building hero sections, or improving the feel of a slow interface."},"skills_sh_url":"https://skills.sh/dembrandt/dembrandt-skills/loading-states-and-perceived-performance"},"updatedAt":"2026-05-18T19:08:27.410Z"}}