skills/andrehogberg/animated-focus/SKILL.md
This document captures learnings from fixing keyboard navigation issues when floating components (Select, DropdownMenu, Popover) have CSS open/close animations.
npx skillsauth add aiskillstore/marketplace animated-focusInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This document captures learnings from fixing keyboard navigation issues when floating components (Select, DropdownMenu, Popover) have CSS open/close animations.
When floating content elements have CSS animations that start at opacity: 0 (like Tailwind's animate-in fade-in-0), the browser may reject element.focus() calls because the element is invisible.
fade-in-0 start the element at opacity: 0focus() is called immediately after render, the element is still invisible// After opening select, keyboard events go to trigger, not content:
Document keydown: ArrowDown Target: <button role="combobox" ...>
// Active element is trigger, not content:
Active: BUTTON summit-select-...-trigger
Implement a retry mechanism for focus that allows the animation to progress past opacity: 0 before giving up.
// src/SummitUI/Scripts/floating.js
/**
* Focus an element with retry mechanism for animated elements.
* Elements with CSS animations starting at opacity:0 may reject focus initially.
* This retries focus up to 5 times with 20ms delays to allow the animation
* to progress past the invisible state.
* @param {HTMLElement} element - Element to focus
*/
export function focusElement(element) {
if (!element) return;
function tryFocus(attempts) {
element.focus();
// If focus didn't succeed and we have attempts left, retry
if (document.activeElement !== element && attempts > 0) {
setTimeout(() => tryFocus(attempts - 1), 20);
}
}
// First attempt after one frame to let CSS apply
requestAnimationFrame(() => tryFocus(5));
}
requestAnimationFrame first - Ensures CSS has been applied before attempting focusdocument.activeElement - Verify if focus actually succeededfocusElement(element) and focusElementById(id) need this patternfloating.js:focusElement(element) - Used by SelectContentfloating.js:focusElementById(elementId) - Used by DropdownMenuContentAdded "With Animations" sections to test demo pages and corresponding Playwright tests.
/* tests/SummitUI.Tests.Manual/SummitUI.Tests.Manual/wwwroot/app.css */
@keyframes fadeInZoomIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes fadeOutZoomOut {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.95);
}
}
.animated-content[data-state="open"] {
animation: fadeInZoomIn 150ms ease-out forwards;
}
.animated-content[data-state="closed"] {
animation: fadeOutZoomOut 150ms ease-in forwards;
}
For each component (Select, DropdownMenu, Popover):
| Test | What It Verifies |
|------|------------------|
| Animated*_ShouldOpen_OnEnterKey | Opens with keyboard when animations present |
| Animated*_ShouldNavigate_WithArrowKeys | Arrow keys work after animated open |
| Animated*_ShouldSelect/Activate_OnEnterKey | Can select/activate item after animated open |
| Animated*_ShouldClose_OnEscape | Escape triggers close animation |
dotnet run --project tests/SummitUI.Tests.Playwright -- --treenode-filter '/*/*/*/Animated*'
| File | Purpose |
|------|---------|
| src/SummitUI/Scripts/floating.js | Contains focusElement and focusElementById functions |
| src/SummitUI/Components/Select/SelectContent.cs | Calls FocusElementAsync on open |
| src/SummitUI/Components/DropdownMenu/DropdownMenuContent.cs | Calls FocusElementByIdAsync for menu items |
| src/SummitUI/Components/Popover/PopoverContent.cs | Manages focus for popover content |
visibility instead of opacity - Would require changes to how animations are authoredThe retry mechanism was chosen because it:
This pattern is similar to how bits-ui handles animated presence in Svelte components. The key insight is that DOM operations (like focus) may need to wait for CSS animations to reach a focusable state.
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.