android-ui-verify
Skill18 starsChanged 4 months agoMITView on GitHub
Verify Android UI against Figma designs or HTML mockups by structural comparison. Compares layout hierarchy, text content, sizing (dp with ±4dp tolerance), and dynamic states using the device's UI Automator tree — never pixel-perfect. Use after finishing UI implementation to validate the result matches the reference. Trigger on: "verify UI", "check the UI", "compare against Figma", "does this match the design", "UI verification", "check layout matches", or when finishing UI implementation work that has a design reference.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- open-ani/animekotheir own version, first seen Oct 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- open-ani/animeko★ 22ksame name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository67 other files it gives its agents.
- agent-onboardingSkill
- android-backgroundSkill
- android-cliSkill
- android-compose-architectureSkill
- android-compose-componentsSkill
- android-data-layerSkill
- android-di-koinSkill
- android-error-handlingSkill
- android-integration-testingSkill
- android-jacoco-setupSkill
- android-jvm-test-coverageSkill
- android-module-structureSkill
- android-navigationSkill
- android-presentation-mviSkill
- android-preview-figma-verifySkill
- android-ui-screenshot-verifySkill
- android-ui-testingSkill
- android-unit-testingSkill
- android-version-catalogSkill
- architecture-decision-recordSkill
- careful-planningSkill
- compose-adaptive-layoutsSkill
- figma-to-android-vectorSkill
- finalizeSkill
- git-commitSkill
- handoffSkill
- kmp-compose-architectureSkill
- kmp-compose-componentsSkill
- kmp-data-layerSkill
- kmp-di-koinSkill
- kmp-error-handlingSkill
- kmp-integration-testingSkill
- kmp-migration-analyzerSkill
- kmp-module-structureSkill
- kmp-navigationSkill
- kmp-presentation-mviSkill
- kmp-ui-testingSkill
- kmp-unit-testingSkill
- kotlin-coroutinesSkill
- kotlin-flowsSkill
- kotlin-mutation-testingSkill
- openspec-artifact-review-lightSkill
- openspec-artifact-reviewSkill
- project-breakdownSkill
- react-data-layerSkill
- react-error-handlingSkill
- react-integration-testingSkill
- react-presentation-mviSkill
- react-project-structureSkill
- react-routingSkill
- react-screen-architectureSkill
- react-securitySkill
- react-server-stateSkill
- react-ui-componentsSkill
- react-unit-testingSkill
- speckit-parallelize-agent-teamsSkill
- speckit-parallelize-subagentsSkill
- spring-api-layerSkill
- spring-di-configurationSkill
- spring-integration-testingSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
47 rules. About 0 are common, 38 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈38
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Run these commands to detect and configure the target device:
- Distinctive: line 52
No device connected: Stop and report error — "No Android device detected.
- Distinctive: line 53
One device: Use it automatically.
- Very specific: line 70
Call the Figma MCP get_design_context tool with the fileKey and nodeId extracted from the URL.
- Distinctive: line 79
Read the HTML file. Parse the DOM to extract:
- Distinctive: line 148
Fail: if a meaningful element is missing or nested under the wrong parent.
- Distinctive: line 171
Every element in the reference design should exist in the device UI tree.
- Distinctive: line 178
CRITICAL: When Step 4 finds a mismatch, do NOT immediately report it as FAIL.
- Very specific: line 193
Read the composable. If it uses LazyColumn, LazyRow, or verticalScroll/horizontalScroll, it WILL scroll when content exceeds the viewport.
- Very specific: line 197
Check if the composable iterates over dynamic data (items(list), forEach).
- Very specific: line 201
Check for conditional rendering: if (state.showX), AnimatedVisibility, state.isLoading.
- Very specific: line 205
Check if the composable uses wrapContentSize, IntrinsicSize, or sizes derived from content/data.
- Very specific: line 206
Report as INFO: if size depends on content: "Card height 64dp vs.
- Very specific: line 217
When a UI Automator element has scrollable="true":
- Distinctive: line 231
Detect end of scroll: If two consecutive dumps show identical element text/resource-ids, the list is fully scrolled.
- Distinctive: line 233
Scroll back to top: when done:
- Distinctive: line 268
After every action: Wait 500ms, then re-dump the UI tree to verify the transition.
- Distinctive: line 270
Scroll slowly: Always use duration >= 500ms for swipes to avoid flinging past content.
- Distinctive: line 277
After navigating to a screen that loads data:
- Distinctive: line 279
If loading indicators are present (ProgressBar, circular indicator, "Loading..." text), poll:
- Distinctive: line 284
If content still hasn't loaded after 5s, report as WARNING (not FAIL) — the backend may be slow.
- Distinctive: line 287
If the calling agent specifies error scenarios:
- Distinctive: line 290
Verify error message text matches the reference
- Distinctive: line 291
Verify error UI structure (e.g., error icon + message in correct container)
- Distinctive: line 294
If the calling agent specifies empty state verification:
- Distinctive: line 295
Ensure the screen has no data loaded
- Distinctive: line 297
Verify empty state elements (illustration, message text) match the reference
- Distinctive: line 303
After all screens are verified structurally, output this JSON report:
- Distinctive: line 348
PASS: All structural checks passed within tolerance.
- Distinctive: line 356
Do NOT attempt to fix code — only report findings.
- Distinctive: line 363
After structural verification, always exercise the features that were built to verify they actually work.
- Very specific: line 385
Before typing: ensure field is focused (tap if needed, verify focused="true")
- Very specific: line 386
Use recognizable test data: "Test Item 12345", "test@example.com", "TestPassword123"
- Distinctive: line 387
After each action, wait 500ms and re-dump UI tree to verify the action took effect
- Distinctive: line 390
Search for the expected content (e.g., the created item's text in the list)
- Distinctive: line 391
If the result requires navigation (e.g., back to list), navigate there first
- Distinctive: line 393
If an action triggers a network call or database write, poll the UI tree (up to 5 attempts, 1s apart) until the expected result appears
- Distinctive: line 394
If result doesn't appear after 5s, check source code — does the save logic require a backend that isn't running?
- Distinctive: line 398
If flows depend on each other, execute them in dependency order:
- Distinctive: line 399
Create before Edit (need an item to edit)
- Distinctive: line 400
Create before Delete (need an item to delete)
- Distinctive: line 401
If the app starts empty, Create flows run first
- Distinctive: line 405
After all flows complete:
- Distinctive: line 407
If cleanup fails, that's OK — don't report cleanup failures
- Distinctive: line 412
If "Save" doesn't navigate back → check if the ViewModel's save logic requires a backend call
- Distinctive: line 413
If a created item doesn't appear in the list → check if the list observes the same data source
- Distinctive: line 414
If a button tap does nothing → check if the click handler is wired up, or if it requires validation that wasn't met
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Android UI Verification
Structurally verify a running Android app's UI against a reference design (Figma or HTML). This skill is invoked by the calling agent after UI implementation is complete.
Philosophy: Android UIs are never pixel-perfect across densities and aspect ratios. This skill compares structure, text, and dp-converted sizing — not colors, shadows, or pixel rendering.
Prerequisites
The calling agent MUST provide:
- What was built — screen names, expected elements, navigation flow
- Reference source — one of:
- A Figma URL (the skill will call
get_design_contextvia MCP) - An HTML file path (the skill will read and parse the DOM)
- A Figma URL (the skill will call
The skill MUST NOT operate on raw screenshots alone — it needs structured component and text data.
Step 1: Device Setup
Run these commands to detect and configure the target device:
# List connected devices
adb devices
# Get screen density (for px → dp conversion)
adb shell wm density
# Example output: "Physical density: 420"
# Get screen resolution
adb shell wm size
# Example output: "Physical size: 1080x2400"Device selection rules
- No device connected: Stop and report error — "No Android device detected. Connect a device or start an emulator."
- One device: Use it automatically.
- Multiple devices: Warn the user and list the device serial numbers. Ask which to use. All subsequent
adbcommands must include-s <serial>.
Density calculation
density_factor = physical_density / 160Store density_factor for all subsequent px → dp conversions.
Step 2: Resolve Reference Design
Figma URL
Call the Figma MCP get_design_context tool with the fileKey and nodeId extracted from the URL. From the response, extract:
- Component names and hierarchy (parent-child nesting)
- Text content of all text nodes
- Frame/component dimensions (width, height) in Figma's logical px (these ARE dp-equivalent)
- Layout direction (horizontal/vertical) and spacing between children
- Whether elements are marked as scrollable or have overflow
HTML file
Read the HTML file. Parse the DOM to extract:
- Element types (div, button, input, span, img, etc.)
- Text content
- CSS width/height values (convert px to dp-equivalent if needed)
- Flex/grid layout direction and gap values
- Visibility and display properties
Build the Reference Model
Regardless of source, normalize into a mental model of:
- Tree structure: Parent → children nesting
- Per element: type, text content, expected width/height in dp, content description
- Layout: direction (row/column), spacing between siblings in dp
- Scrollable regions: which containers scroll and what content they hold
Step 3: Capture UI Tree from Device
adb exec-out uiautomator dump /dev/ttyThis returns XML. Parse each node element and extract:
| XML Attribute | Use |
class | Element type (e.g., android.widget.TextView, android.widget.Button) |
text | Visible text content |
resource-id | Android resource ID (best identifier) |
content-desc | Accessibility description |
bounds | Pixel bounds as [left,top][right,bottom] |
scrollable | Whether the element scrolls |
clickable | Whether the element is tappable |
enabled | Whether the element is interactive |
focused | Whether the element has input focus |
Convert bounds to dp
From bounds="[left,top][right,bottom]":
width_dp = (right - left) / density_factor
height_dp = (bottom - top) / density_factor
x_dp = left / density_factor
y_dp = top / density_factorElement identification (best-effort)
Match elements between reference and device using this priority:
resource-id(most reliable)textcontent (exact match)content-desc(accessibility label)- Element type + position in hierarchy (least reliable)
If an element cannot be reliably identified (no resource-id, no text, no content-desc, and ambiguous position), log a warning:
"Element at [bounds] has no resource-id, text, or content-desc — consider adding a
testTagor semantics modifier for reliable identification."
Continue verification anyway — do not stop.
Step 4: Structural Comparison
Compare the reference model against the device UI tree. Check each of these aspects:
Hierarchy
- Parent-child nesting should match semantically.
- Allow intermediate wrapper nodes that exist on Android but not in the design (e.g.,
Viewcontainers wrappingComposeView,FrameLayoutwrappers). - Fail if a meaningful element is missing or nested under the wrong parent.
Text content
- Exact case-sensitive string match for all visible text.
- For dynamic content (timestamps, counts, user-specific data): verify the text field exists and is non-empty rather than matching exact value. The calling agent should specify which fields are dynamic.
Element types
- Map reference types to Android classes semantically. See comparison-rules.md for the full mapping.
- Example: a Figma "Text" node matches
android.widget.TextView; a "Frame with click handler" matches a clickableVieworButton.
Sizing
- Compare width and height in dp.
- Tolerance: ±4dp. Report as FAIL only if delta exceeds 4dp.
- Skip size comparison for elements that are
match_parent/fillMaxWidth(full-width elements) — compare them proportionally instead (should be close to screen width).
Spacing
- Calculate spacing between consecutive siblings:
next_element.y_dp - (current_element.y_dp + current_element.height_dp)for vertical layouts. - Tolerance: ±4dp.
Sibling order
- Children should appear in the same order as the reference design.
Visibility
- Every element in the reference design should exist in the device UI tree.
- Missing elements are a FAIL — unless source code verification (Step 4b) reclassifies them.
Step 4b: Source Code Verification for Mismatches
CRITICAL: When Step 4 finds a mismatch, do NOT immediately report it as FAIL. First, cross-reference the relevant Compose source code to determine if the mismatch is a real bug or a data/state-dependent difference.
The calling agent already knows which composables were built. Use Grep to find the composable by name, then Read the file to understand the layout logic.
Classification
| After reading source code... | Report as | Action |
| The mismatch is a real layout bug (wrong modifier, missing element in code) | FAIL | Include in report |
| The mismatch is data-dependent (fewer items, empty state, conditional visibility) | INFO | Include in report with explanation of why it's not a bug |
| The mismatch is expected Android behavior (system wrappers, density rounding) | — | Suppress entirely |
Specific scenarios to verify
- Scrollable mismatch — Reference shows scrollable list, device shows
scrollable="false":- Read the composable. If it uses
LazyColumn,LazyRow, orverticalScroll/horizontalScroll, it WILL scroll when content exceeds the viewport. The current data simply doesn't trigger scrolling. - Report as INFO: "LazyColumn in
HabitListScreen.kt:42— not scrollable because current item count (3) fits within viewport. Will scroll with more items."
- Read the composable. If it uses
- Missing elements — Reference shows N items, device shows fewer:
- Check if the composable iterates over dynamic data (
items(list),forEach). If so, the count depends on runtime data. - Report as INFO: "List shows 3 items vs. 8 in reference — composable uses
items(habits)atHabitListScreen.kt:55, count is data-dependent."
- Check if the composable iterates over dynamic data (
- Visibility mismatch — Reference shows an element that's absent on device:
- Check for conditional rendering:
if (state.showX),AnimatedVisibility,state.isLoading. - Report as INFO: "Empty state illustration not shown — guarded by
if (habits.isEmpty())atHabitListScreen.kt:30, currently has data."
- Check for conditional rendering:
- Size mismatch beyond tolerance — Element significantly larger/smaller than reference:
- Check if the composable uses
wrapContentSize,IntrinsicSize, or sizes derived from content/data. - Report as INFO if size depends on content: "Card height 64dp vs. expected 96dp — uses
wrapContentHeight(), height depends on text length."
- Check if the composable uses
When NOT to check source code
- Text content mismatches (wrong string) — these are always real issues.
- Sibling order mismatches — layout order is code-determined, not data-dependent.
- Element type mismatches (Button vs. Text) — always a real issue.
Step 5: Scrollable Content
When a UI Automator element has scrollable="true":
- Verify visible content first — run structural comparison on what's currently on screen.
- Calculate scroll bounds from the scrollable element's
bounds. - Scroll slowly:
# Scroll down: swipe from bottom-center to top-center of the scrollable area adb shell input swipe <centerX> <bottom_80%> <centerX> <top_20%> 800 - Wait 500ms, then re-dump the UI tree:
sleep 0.5 && adb exec-out uiautomator dump /dev/tty - Verify newly visible elements against the reference.
- Detect end of scroll: If two consecutive dumps show identical element text/resource-ids, the list is fully scrolled.
- Repeat up to 20 scroll iterations (safety limit).
- Scroll back to top when done:
# Swipe down repeatedly to return to top adb shell input swipe <centerX> <top_20%> <centerX> <bottom_80%> 800
Step 6: Navigation & Interaction
The calling agent provides the flow to verify (e.g., "tap Login, enter email 'test@test.com', tap Submit, verify Dashboard screen").
Interaction commands
# Tap at center of target element
adb shell input tap <x> <y>
# Type text into focused field
adb shell input text "<text>"
# Special characters / spaces: use keyevents
adb shell input keyevent 62 # space
adb shell input keyevent 66 # enter
# Back button
adb shell input keyevent 4
# Swipe
adb shell input swipe <x1> <y1> <x2> <y2> <duration_ms>Interaction rules
- Before typing: Dump UI tree and confirm the target field has
focused="true". If not, tap the field first, wait 300ms, re-dump, and verify focus. - After every action: Wait 500ms, then re-dump the UI tree to verify the transition.
- If a screen doesn't change after an action: Retry once after 1s. If still unchanged, log it and continue.
- Scroll slowly: Always use duration >= 500ms for swipes to avoid flinging past content.
Step 7: Dynamic State Verification
Loading → Content transitions
After navigating to a screen that loads data:
- Dump UI tree immediately.
- If loading indicators are present (ProgressBar, circular indicator, "Loading..." text), poll:
sleep 1 && adb exec-out uiautomator dump /dev/tty - Repeat up to 5 times (5 seconds total wait).
- If content still hasn't loaded after 5s, report as WARNING (not FAIL) — the backend may be slow.
Error states
If the calling agent specifies error scenarios:
- Trigger the error condition (e.g., enter invalid input, the calling agent should set up mock error responses)
- Dump UI tree
- Verify error message text matches the reference
- Verify error UI structure (e.g., error icon + message in correct container)
Empty states
If the calling agent specifies empty state verification:
- Ensure the screen has no data loaded
- Dump UI tree
- Verify empty state elements (illustration, message text) match the reference
Step 8: Report Structural Results
After all screens are verified structurally, output this JSON report:
{
"device": {
"density": 420,
"resolution": "1080x2400",
"density_factor": 2.625
},
"screens": [
{
"name": "Screen Name",
"reference_source": "figma://file/KEY?node=X:Y or path/to/file.html",
"status": "PASS | PARTIAL | FAIL",
"checks": [
{
"element": "Element description",
"check": "What was compared (e.g., 'height', 'text content', 'presence')",
"status": "PASS | FAIL | INFO",
"expected": "Expected value",
"actual": "Actual value",
"delta_dp": null,
"source_verified": "LazyColumn in HabitListScreen.kt:42 — not scrollable because item count fits viewport"
}
],
"warnings": [
"Warning messages (unidentifiable elements, slow loads, etc.)"
]
}
],
"summary": {
"screens_verified": 0,
"passed": 0,
"partial": 0,
"failed": 0,
"total_checks": 0,
"checks_passed": 0,
"checks_failed": 0,
"checks_info": 0,
"warnings": 0
}
}Status definitions
- PASS: All structural checks passed within tolerance.
- PARTIAL: Structure matches but some sizing/spacing checks exceeded ±4dp tolerance.
- FAIL: Missing elements, wrong text, or broken hierarchy — confirmed as real bug after source code verification.
- INFO: Mismatch detected but source code verification confirmed it's data-dependent or expected behavior. Not a bug.
After reporting structural results
- Continue to the next screen even if the current one fails — report ALL screens.
- INFO checks do NOT count toward failure — they are context, not problems.
- Do NOT attempt to fix code — only report findings.
- Proceed to Step 9 (Behavioral Verification) after structural checks are complete.
Step 9: Behavioral Verification
After structural verification, always exercise the features that were built to verify they actually work. This step catches functional bugs that structural checks cannot detect.
Deriving test flows
The calling agent knows what features were built. Based on that context, derive testable user flows. Common patterns:
| Feature type | Test flow |
| Create screen (form) | Fill all fields → tap save/submit → navigate to list → verify new item appears |
| List screen | Verify items display → tap an item → verify detail screen opens with correct data |
| Edit screen | Open existing item → change a field → save → verify change persists |
| Delete action | Trigger delete (swipe, long-press, button) → confirm → verify item is removed |
| Login/Auth | Enter credentials → submit → verify navigation to authenticated screen |
| Search/Filter | Enter search query → verify filtered results → clear → verify full list returns |
| Toggle/Switch | Tap toggle → verify state change → tap again → verify it reverts |
Executing flows
For each test flow:
- Navigate to the starting screen using the interaction commands from Step 6.
- Perform actions sequentially:
- Before typing: ensure field is focused (tap if needed, verify
focused="true") - Use recognizable test data:
"Test Item 12345","test@example.com","TestPassword123" - After each action, wait 500ms and re-dump UI tree to verify the action took effect
- Before typing: ensure field is focused (tap if needed, verify
- Verify the outcome:
- Re-dump the UI tree on the result screen
- Search for the expected content (e.g., the created item's text in the list)
- If the result requires navigation (e.g., back to list), navigate there first
- Handle async operations:
- If an action triggers a network call or database write, poll the UI tree (up to 5 attempts, 1s apart) until the expected result appears
- If result doesn't appear after 5s, check source code — does the save logic require a backend that isn't running? Report as INFO if it's an environment issue, FAIL if the code is wrong.
Ordering flows
If flows depend on each other, execute them in dependency order:
- Create before Edit (need an item to edit)
- Create before Delete (need an item to delete)
- If the app starts empty, Create flows run first
Cleanup
After all flows complete:
- Attempt to delete test items if a delete mechanism exists
- If cleanup fails, that's OK — don't report cleanup failures
Source code verification for behavioral failures
Apply the same Step 4b logic to behavioral failures:
- If "Save" doesn't navigate back → check if the ViewModel's save logic requires a backend call
- If a created item doesn't appear in the list → check if the list observes the same data source
- If a button tap does nothing → check if the click handler is wired up, or if it requires validation that wasn't met
Behavioral report
Append to the same report JSON:
{
"behavioral": [
{
"flow": "Create a habit named 'Test Item 12345'",
"steps": [
{
"action": "Tap 'Add Habit' FAB",
"status": "PASS",
"commands": ["adb shell input tap 540 2200"]
},
{
"action": "Enter 'Test Item 12345' in name field",
"status": "PASS",
"commands": ["adb shell input tap 540 400", "adb shell input text 'Test Item 12345'"]
},
{
"action": "Tap 'Save' button",
"status": "PASS",
"commands": ["adb shell input tap 540 1800"]
},
{
"action": "Verify 'Test Item 12345' appears in habit list",
"status": "PASS",
"source_verified": null
}
],
"status": "PASS"
}
],
"behavioral_summary": {
"flows_tested": 1,
"flows_passed": 1,
"flows_failed": 0,
"flows_info": 0
}
}Quick Reference: ADB Commands
| Action | Command |
| List devices | adb devices |
| Get density | adb shell wm density |
| Get resolution | adb shell wm size |
| UI dump | adb exec-out uiautomator dump /dev/tty |
| Tap | adb shell input tap <x> <y> |
| Type text | adb shell input text "<text>" |
| Swipe/scroll | adb shell input swipe <x1> <y1> <x2> <y2> <ms> |
| Back | adb shell input keyevent 4 |
| Home | adb shell input keyevent 3 |
| Enter | adb shell input keyevent 66 |
| Space | adb shell input keyevent 62 |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

