Use this skill to verify Compose layout measurements from a UI test using `assertWidthIsEqualTo`, `assertHeightIsEqualTo`, `assertWidthIsAtLeast`, `assertHeightIsAtLeast`, `assertTouchWidthIsEqualTo`, `assertTouchHeightIsEqualTo`, `assertPositionInRootIsEqualTo`, `assertTopPositionInRootIsEqualTo`, `assertLeftPositionInRootIsEqualTo`, plus read helpers `getUnclippedBoundsInRoot`, `getBoundsInRoot`, `getAlignmentLinePosition`, `getFirstLinkBounds`, and the underlying `Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f))`. Covers the half-dp default tolerance, the unclipped vs clipped distinction, the canonical "compute padding from two unclipped rects" pattern, and minimum-touch-target assertions like `assertHeightIsAtLeast(MinHeight + 1.dp)`. Use when the developer wants to assert sizes, padding, alignment, position in dp, or asks about `getUnclippedBoundsInRoot`, `DpRect`, touch-target size, or compares widths in pixels. If the developer is comparing layout dimensions from a test, use this skill.
Asserting Bounds and Dimensions — Layout Math in Dp, Not Pixels
Layout assertions belong in dp, run with a half-dp tolerance, and most of the interesting checks (padding, gap, alignment) are subtractions between two getUnclippedBoundsInRoot() rectangles. This skill picks the right size/position assertion, explains clipped vs unclipped, and shows the canonical "compute padding from two rects" pattern lifted directly from material3/ButtonTest.kt.
When to use this skill
The developer wants to verify a Button is 48 dp tall, a Spacer is 16 dp wide, an Icon is at position (24.dp, 12.dp).
The developer asks how to assert the padding between two composables.
The developer asks about minimum touch target sizes (ChipDefaults.MinHeight + 1.dp).
The developer is comparing layout values in pixels and wants the dp-typed equivalent.
The developer mentions assertWidthIsEqualTo, getUnclippedBoundsInRoot, DpRect, getAlignmentLinePosition, getFirstLinkBounds.
When NOT to use this skill
The check is about state (enabled, on, selected) — see ./asserting-node-state-and-text/SKILL.md.
The check is "is the node on screen at all" — assertIsDisplayed() is enough; bounds math adds friction without value.
The composable's bounds depend on an animation in flight — pause the clock first; see ../../synchronization/testing-animations-deterministically/SKILL.md.
The bounds are relative to a screenshot — use a screenshot test instead.
Prerequisites
A working ComposeTestRule / ComposeUiTest. See ../../setup/configuring-test-dependencies/SKILL.md.
The target composable has finished measuring and placing. If it animates in, advance the test clock first — see ../../synchronization/controlling-the-test-clock/SKILL.md.
For touch-target assertions, the target node has a click action so touchBoundsInRoot is meaningful.
Workflow
1. Pick the assertion by question type. All APIs live in commonMain/.../BoundsAssertions.kt.
2. Use unclipped bounds for layout math; clipped bounds for "what the user sees".getUnclippedBoundsInRoot() returns the laid-out rectangle ignoring viewport clipping (BoundsAssertions.kt:148-152 → unclippedBoundsInRoot private, BoundsAssertions.kt:284-291). getBoundsInRoot() clips to the viewport (BoundsAssertions.kt:158-165). Padding/spacing math uses unclipped; partial-visibility checks use clipped.
3. Tolerance is half a dp by default.Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f)) (BoundsAssertions.kt:319) accepts deviations up to 0.5 dp because layout rounding introduces sub-dp drift. Override only when stricter precision is justified by an explicit measurement contract.
4. Compute padding by subtracting two unclipped rects, then call Dp.assertIsEqualTo. This is the canonical material3 pattern (material3/.../ButtonTest.kt:213-225):
val buttonBounds = rule.onNodeWithTag(ButtonTestTag).getUnclippedBoundsInRoot()
val textBounds = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot()
(textBounds.left - buttonBounds.left).assertIsEqualTo(
24.dp,
"padding between the start of the button and the start of the text.",
)
(buttonBounds.right - textBounds.right).assertIsEqualTo(
24.dp,
"padding between the end of the text and the end of the button.",
)
buttonBounds.height.assertIsEqualTo(ButtonDefaults.MinHeight, "height of button.")
The subject string lands in the failure message: "Actual padding between the start of the button and the start of the text. is 22.dp, expected 24.dp (tolerance: .5.dp)".
5. For minimum-size contracts, use assertHeightIsAtLeast / assertWidthIsAtLeast. Useful when a composable should never be smaller than a constant, even at large font scale. Example from material/ChipTest.kt:226-233:
6. Use touch bounds when verifying tap-target accessibility.assertTouchWidthIsEqualTo / assertTouchHeightIsEqualTo reads node.touchBoundsInRoot (BoundsAssertions.kt:270-282), which can extend past the visual bounds when the composable applies Modifier.minimumInteractiveComponentSize() or similar. Visual bounds use getUnclippedBoundsInRoot; touch bounds answer "where will a click land".
Patterns
Pattern: dp typed assertions over pixel reads
// WRONG
@Test
fun submit_isMin48dpTall() {
rule.setContent { CheckoutScreen() }
val node = rule.onNodeWithTag(SubmitTag).fetchSemanticsNode()
val heightPx = node.size.height
assert(heightPx >= 48 * Resources.getSystem().displayMetrics.density)
}
// WRONG because: pixel-typed and density-dependent. Reads outside the framework's tolerance
// model. Failure prints raw integers, no node dump, no subject label.
// RIGHT
@Test
fun submit_isMin48dpTall() {
rule.setContent { CheckoutScreen() }
rule.onNodeWithTag(SubmitTag).assertHeightIsAtLeast(48.dp)
}
Pattern: padding by subtracting two unclipped rects
The merge bypass on the inner Text is the same trick used by material3/ButtonTest.kt:202-226 to keep the inner Text addressable from the merged tree.
Pattern: assertPositionInRootIsEqualTo for absolute placement
// "the close button sits at (320.dp, 0.dp) in the root"
rule.onNodeWithTag(CloseButtonTag)
.assertPositionInRootIsEqualTo(expectedLeft = 320.dp, expectedTop = 0.dp)
If only one axis matters, use assertLeftPositionInRootIsEqualTo / assertTopPositionInRootIsEqualTo to avoid coupling the test to layout decisions on the other axis.
Pattern: alignment line for baseline math
val baselineDp = rule.onNodeWithTag(LabelTag)
.getAlignmentLinePosition(FirstBaseline)
require(!baselineDp.isUnspecified) { "Label has no first baseline" }
baselineDp.assertIsEqualTo(20.dp, "first baseline of label")
getAlignmentLinePosition returns Dp.Unspecified when the alignment line is not provided (BoundsAssertions.kt:172-179). Always check isUnspecified before comparing.
Pattern: tolerance override for sub-dp precision
// Most tests want the default ½ dp tolerance:
rule.onNodeWithTag(IconTag).getUnclippedBoundsInRoot().width.assertIsEqualTo(24.dp, "icon width")
// Stricter tolerance when measuring a hand-aligned constant:
val width = rule.onNodeWithTag(IconTag).getUnclippedBoundsInRoot().width
width.assertIsEqualTo(expected = 24.dp, subject = "icon width", tolerance = 0.1.dp)
Pattern: clipped vs unclipped — partial visibility
val unclipped = rule.onNodeWithTag(BannerTag).getUnclippedBoundsInRoot()
val clipped = rule.onNodeWithTag(BannerTag).getBoundsInRoot()
// Banner laid out 200 dp tall but only 80 dp visible (rest clipped by parent):
unclipped.height.assertIsEqualTo(200.dp, "banner intrinsic height")
clipped.height.assertIsEqualTo(80.dp, "banner visible height")
For "is any of it visible", prefer assertIsDisplayed() — see ./asserting-node-state-and-text/SKILL.md.
Mandatory rules
MUST assert in dp using the typed assertWidthIsEqualTo / assertHeightIsEqualTo / assertPositionInRootIsEqualTo. MUST NOT read fetchSemanticsNode().size.width and compare pixels.
MUST use getUnclippedBoundsInRoot() for padding / gap / alignment math; MUST use getBoundsInRoot() only when the contract is "what the user sees after clipping".
MUST pass a meaningful subject string to Dp.assertIsEqualTo so the failure message identifies which measurement failed.
MUST prefer assertHeightIsAtLeast(MinHeight + 1.dp) over assertHeightIsEqualTo(MinHeight + N.dp) when the goal is "the layout grows past the minimum at large font scales".
MUST NOT assume zero tolerance. Layout rounding produces sub-dp drift; rely on the half-dp default and override only when justified.
PREFERRED: when an animation is in flight, pause mainClock.autoAdvance = false and step deterministically before reading bounds. Skydoves hot take #3.
Verification
No node.size.width / node.size.height reads remain. All dimension checks use the typed assert*IsEqualTo / Dp.assertIsEqualTo.
Padding / gap math uses getUnclippedBoundsInRoot; clipped reads only appear with a comment explaining why.
Every Dp.assertIsEqualTo(...) passes a non-empty subject string.
Touch-target assertions use assertTouchWidthIsEqualTo / assertTouchHeightIsEqualTo rather than visual bounds when verifying accessibility constraints.
./gradlew :app:connectedDebugAndroidTest passes; failure messages identify the specific failed measurement by subject.
compose/material3/material3/src/androidDeviceTest/kotlin/androidx/compose/material3/ButtonTest.kt:202-226 — canonical "subtract two unclipped rects" padding test.
compose/material/material/src/androidDeviceTest/kotlin/androidx/compose/material/ChipTest.kt:226-233 — assertHeightIsAtLeast(MinHeight + 1.dp) for minimum-touch contracts.
1---2name: asserting-bounds-and-dimensions3description: Use this skill to verify Compose layout measurements from a UI test using `assertWidthIsEqualTo`, `assertHeightIsEqualTo`, `assertWidthIsAtLeast`, `assertHeightIsAtLeast`, `assertTouchWidthIsEqualTo`, `assertTouchHeightIsEqualTo`, `assertPositionInRootIsEqualTo`, `assertTopPositionInRootIsEqualTo`, `assertLeftPositionInRootIsEqualTo`, plus read helpers `getUnclippedBoundsInRoot`, `getBoundsInRoot`, `getAlignmentLinePosition`, `getFirstLinkBounds`, and the underlying `Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f))`. Covers the half-dp default tolerance, the unclipped vs clipped distinction, the canonical "compute padding from two unclipped rects" pattern, and minimum-touch-target assertions like `assertHeightIsAtLeast(MinHeight + 1.dp)`. Use when the developer wants to assert sizes, padding, alignment, position in dp, or asks about `getUnclippedBoundsInRoot`, `DpRect`, touch-target size, or compares widths in pixels. If the developer is comparing layout dimensions from a test, use this skill.4license: Apache-2.0. See LICENSE for complete terms.5---67# Asserting Bounds and Dimensions — Layout Math in Dp, Not Pixels89Layout assertions belong in dp, run with a half-dp tolerance, and most of the interesting checks (padding, gap, alignment) are subtractions between two `getUnclippedBoundsInRoot()` rectangles. This skill picks the right size/position assertion, explains clipped vs unclipped, and shows the canonical "compute padding from two rects" pattern lifted directly from `material3/ButtonTest.kt`.1011## When to use this skill1213- The developer wants to verify a Button is 48 dp tall, a Spacer is 16 dp wide, an Icon is at position `(24.dp, 12.dp)`.14- The developer asks how to assert the padding between two composables.15- The developer asks about minimum touch target sizes (`ChipDefaults.MinHeight + 1.dp`).16- The developer is comparing layout values in pixels and wants the dp-typed equivalent.17- The developer mentions `assertWidthIsEqualTo`, `getUnclippedBoundsInRoot`, `DpRect`, `getAlignmentLinePosition`, `getFirstLinkBounds`.1819## When NOT to use this skill2021- The check is about state (enabled, on, selected) — see `./asserting-node-state-and-text/SKILL.md`.22- The check is "is the node on screen at all" — `assertIsDisplayed()` is enough; bounds math adds friction without value.23- The composable's bounds depend on an animation in flight — pause the clock first; see `../../synchronization/testing-animations-deterministically/SKILL.md`.24- The bounds are relative to a screenshot — use a screenshot test instead.2526## Prerequisites2728- A working `ComposeTestRule` / `ComposeUiTest`. See `../../setup/configuring-test-dependencies/SKILL.md`.29- The target composable has finished measuring and placing. If it animates in, advance the test clock first — see `../../synchronization/controlling-the-test-clock/SKILL.md`.30- For touch-target assertions, the target node has a click action so `touchBoundsInRoot` is meaningful.3132## Workflow3334- [ ] **1. Pick the assertion by question type.** All APIs live in `commonMain/.../BoundsAssertions.kt`.3536 | Question | API | File:line |37 |---|---|---|38 | Is the layout exactly W dp wide? | `assertWidthIsEqualTo(expectedWidth: Dp)` | `BoundsAssertions.kt:44-46` |39 | Is the layout exactly H dp tall? | `assertHeightIsEqualTo(expectedHeight: Dp)` | `BoundsAssertions.kt:53-55` |40 | At least W wide? | `assertWidthIsAtLeast(expectedMinWidth: Dp)` | `BoundsAssertions.kt:85-87` |41 | At least H tall? | `assertHeightIsAtLeast(expectedMinHeight: Dp)` | `BoundsAssertions.kt:95-99` |42 | Touch-target width? | `assertTouchWidthIsEqualTo(expectedWidth: Dp)` | `BoundsAssertions.kt:62-66` |43 | Touch-target height? | `assertTouchHeightIsEqualTo(expectedHeight: Dp)` | `BoundsAssertions.kt:73-77` |44 | Exact position in root? | `assertPositionInRootIsEqualTo(left: Dp, top: Dp)` | `BoundsAssertions.kt:109-117` |45 | Top position only? | `assertTopPositionInRootIsEqualTo(top: Dp)` | `BoundsAssertions.kt:126-130` |46 | Left position only? | `assertLeftPositionInRootIsEqualTo(left: Dp)` | `BoundsAssertions.kt:139-143` |47 | Read full unclipped bounds | `getUnclippedBoundsInRoot(): DpRect` | `BoundsAssertions.kt:148-152` |48 | Read clipped bounds | `getBoundsInRoot(): DpRect` | `BoundsAssertions.kt:158-165` |49 | Alignment line in dp | `getAlignmentLinePosition(line: AlignmentLine): Dp` | `BoundsAssertions.kt:171-180` |50 | Bounds of a `LinkAnnotation` in a Text | `getFirstLinkBounds(predicate)` | `BoundsAssertions.kt:196-248` |51 | Compare any two `Dp` values | `Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f))` | `BoundsAssertions.kt:319-324` |5253- [ ] **2. Use unclipped bounds for layout math; clipped bounds for "what the user sees".** `getUnclippedBoundsInRoot()` returns the laid-out rectangle ignoring viewport clipping (`BoundsAssertions.kt:148-152` → `unclippedBoundsInRoot` private, `BoundsAssertions.kt:284-291`). `getBoundsInRoot()` clips to the viewport (`BoundsAssertions.kt:158-165`). Padding/spacing math uses unclipped; partial-visibility checks use clipped.5455- [ ] **3. Tolerance is half a dp by default.** `Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f))` (`BoundsAssertions.kt:319`) accepts deviations up to 0.5 dp because layout rounding introduces sub-dp drift. Override only when stricter precision is justified by an explicit measurement contract.5657- [ ] **4. Compute padding by subtracting two unclipped rects, then call `Dp.assertIsEqualTo`.** This is the canonical material3 pattern (`material3/.../ButtonTest.kt:213-225`):5859 ```kotlin60 val buttonBounds = rule.onNodeWithTag(ButtonTestTag).getUnclippedBoundsInRoot()61 val textBounds = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot()6263 (textBounds.left - buttonBounds.left).assertIsEqualTo(64 24.dp,65 "padding between the start of the button and the start of the text.",66 )6768 (buttonBounds.right - textBounds.right).assertIsEqualTo(69 24.dp,70 "padding between the end of the text and the end of the button.",71 )72 buttonBounds.height.assertIsEqualTo(ButtonDefaults.MinHeight, "height of button.")73 ```7475 The `subject` string lands in the failure message: `"Actual padding between the start of the button and the start of the text. is 22.dp, expected 24.dp (tolerance: .5.dp)"`.7677- [ ] **5. For minimum-size contracts, use `assertHeightIsAtLeast` / `assertWidthIsAtLeast`.** Useful when a composable should never be smaller than a constant, even at large font scale. Example from `material/ChipTest.kt:226-233`:7879 ```kotlin80 rule.setMaterialContent { Chip(onClick = {}) { Text(text = "Test chip", fontSize = 50.sp) } }81 rule.onNode(hasClickAction()).assertHeightIsAtLeast(ChipDefaults.MinHeight + 1.dp)82 ```8384- [ ] **6. Use touch bounds when verifying tap-target accessibility.** `assertTouchWidthIsEqualTo` / `assertTouchHeightIsEqualTo` reads `node.touchBoundsInRoot` (`BoundsAssertions.kt:270-282`), which can extend past the visual bounds when the composable applies `Modifier.minimumInteractiveComponentSize()` or similar. Visual bounds use `getUnclippedBoundsInRoot`; touch bounds answer "where will a click land".8586## Patterns8788### Pattern: dp typed assertions over pixel reads8990```kotlin91// WRONG92@Test93fun submit_isMin48dpTall() {94 rule.setContent { CheckoutScreen() }95 val node = rule.onNodeWithTag(SubmitTag).fetchSemanticsNode()96 val heightPx = node.size.height97 assert(heightPx >= 48 * Resources.getSystem().displayMetrics.density)98}99// WRONG because: pixel-typed and density-dependent. Reads outside the framework's tolerance100// model. Failure prints raw integers, no node dump, no subject label.101```102103```kotlin104// RIGHT105@Test106fun submit_isMin48dpTall() {107 rule.setContent { CheckoutScreen() }108 rule.onNodeWithTag(SubmitTag).assertHeightIsAtLeast(48.dp)109}110```111112### Pattern: padding by subtracting two unclipped rects113114```kotlin115@Test116fun button_text_has24dpPadding() {117 rule.setContent {118 Button(onClick = {}, modifier = Modifier.testTag(ButtonTestTag)) {119 Text("Submit", modifier = Modifier.testTag(TextTestTag).semantics(mergeDescendants = true) {})120 }121 }122123 val buttonBounds = rule.onNodeWithTag(ButtonTestTag).getUnclippedBoundsInRoot()124 val textBounds = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot()125126 (textBounds.left - buttonBounds.left).assertIsEqualTo(24.dp, "start padding")127 (buttonBounds.right - textBounds.right).assertIsEqualTo(24.dp, "end padding")128}129```130131The merge bypass on the inner `Text` is the same trick used by `material3/ButtonTest.kt:202-226` to keep the inner Text addressable from the merged tree.132133### Pattern: `assertPositionInRootIsEqualTo` for absolute placement134135```kotlin136// "the close button sits at (320.dp, 0.dp) in the root"137rule.onNodeWithTag(CloseButtonTag)138 .assertPositionInRootIsEqualTo(expectedLeft = 320.dp, expectedTop = 0.dp)139```140141If only one axis matters, use `assertLeftPositionInRootIsEqualTo` / `assertTopPositionInRootIsEqualTo` to avoid coupling the test to layout decisions on the other axis.142143### Pattern: alignment line for baseline math144145```kotlin146val baselineDp = rule.onNodeWithTag(LabelTag)147 .getAlignmentLinePosition(FirstBaseline)148require(!baselineDp.isUnspecified) { "Label has no first baseline" }149baselineDp.assertIsEqualTo(20.dp, "first baseline of label")150```151152`getAlignmentLinePosition` returns `Dp.Unspecified` when the alignment line is not provided (`BoundsAssertions.kt:172-179`). Always check `isUnspecified` before comparing.153154### Pattern: tolerance override for sub-dp precision155156```kotlin157// Most tests want the default ½ dp tolerance:158rule.onNodeWithTag(IconTag).getUnclippedBoundsInRoot().width.assertIsEqualTo(24.dp, "icon width")159160// Stricter tolerance when measuring a hand-aligned constant:161val width = rule.onNodeWithTag(IconTag).getUnclippedBoundsInRoot().width162width.assertIsEqualTo(expected = 24.dp, subject = "icon width", tolerance = 0.1.dp)163```164165### Pattern: clipped vs unclipped — partial visibility166167```kotlin168val unclipped = rule.onNodeWithTag(BannerTag).getUnclippedBoundsInRoot()169val clipped = rule.onNodeWithTag(BannerTag).getBoundsInRoot()170171// Banner laid out 200 dp tall but only 80 dp visible (rest clipped by parent):172unclipped.height.assertIsEqualTo(200.dp, "banner intrinsic height")173clipped.height.assertIsEqualTo(80.dp, "banner visible height")174```175176For "is any of it visible", prefer `assertIsDisplayed()` — see `./asserting-node-state-and-text/SKILL.md`.177178## Mandatory rules179180- **MUST** assert in dp using the typed `assertWidthIsEqualTo` / `assertHeightIsEqualTo` / `assertPositionInRootIsEqualTo`. **MUST NOT** read `fetchSemanticsNode().size.width` and compare pixels.181- **MUST** use `getUnclippedBoundsInRoot()` for padding / gap / alignment math; **MUST** use `getBoundsInRoot()` only when the contract is "what the user sees after clipping".182- **MUST** pass a meaningful `subject` string to `Dp.assertIsEqualTo` so the failure message identifies which measurement failed.183- **MUST** prefer `assertHeightIsAtLeast(MinHeight + 1.dp)` over `assertHeightIsEqualTo(MinHeight + N.dp)` when the goal is "the layout grows past the minimum at large font scales".184- **MUST NOT** assume zero tolerance. Layout rounding produces sub-dp drift; rely on the half-dp default and override only when justified.185- **PREFERRED:** when an animation is in flight, pause `mainClock.autoAdvance = false` and step deterministically before reading bounds. Skydoves hot take #3.186187## Verification188189- [ ] No `node.size.width` / `node.size.height` reads remain. All dimension checks use the typed `assert*IsEqualTo` / `Dp.assertIsEqualTo`.190- [ ] Padding / gap math uses `getUnclippedBoundsInRoot`; clipped reads only appear with a comment explaining why.191- [ ] Every `Dp.assertIsEqualTo(...)` passes a non-empty `subject` string.192- [ ] Touch-target assertions use `assertTouchWidthIsEqualTo` / `assertTouchHeightIsEqualTo` rather than visual bounds when verifying accessibility constraints.193- [ ] `./gradlew :app:connectedDebugAndroidTest` passes; failure messages identify the specific failed measurement by `subject`.194195## References196197- Compose testing overview: https://developer.android.com/develop/ui/compose/testing198- Compose testing cheat sheet: https://developer.android.com/develop/ui/compose/testing-cheatsheet199- Layout in Compose: https://developer.android.com/develop/ui/compose/layouts200- `compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/BoundsAssertions.kt` — `assertWidthIsEqualTo`, `assertHeightIsAtLeast`, `assertPositionInRootIsEqualTo`, `getUnclippedBoundsInRoot`, `getBoundsInRoot`, `getAlignmentLinePosition`, `getFirstLinkBounds`, `Dp.assertIsEqualTo` (default tolerance ½ dp).201- `compose/material3/material3/src/androidDeviceTest/kotlin/androidx/compose/material3/ButtonTest.kt:202-226` — canonical "subtract two unclipped rects" padding test.202- `compose/material/material/src/androidDeviceTest/kotlin/androidx/compose/material/ChipTest.kt:226-233` — `assertHeightIsAtLeast(MinHeight + 1.dp)` for minimum-touch contracts.
Run npx skillmds@latest add skydoves/asserting-bounds-and-dimensions in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use this skill to verify Compose layout measurements from a UI test using `assertWidthIsEqualTo`, `assertHeightIsEqualTo`, `assertWidthIsAtLeast`, `assertHeightIsAtLeast`, `assertTouchWidthIsEqualTo`, `assertTouchHeightIsEqualTo`, `assertPositionInRootIsEqualTo`, `assertTopPositionInRootIsEqualTo`, `assertLeftPositionInRootIsEqualTo`, plus read helpers `getUnclippedBoundsInRoot`, `getBoundsInRoot`, `getAlignmentLinePosition`, `getFirstLinkBounds`, and the underlying `Dp.assertIsEqualTo(expected, subject, tolerance = Dp(.5f))`. Covers the half-dp default tolerance, the unclipped vs clipped distinction, the canonical "compute padding from two unclipped rects" pattern, and minimum-touch-target assertions like `assertHeightIsAtLeast(MinHeight + 1.dp)`. Use when the developer wants to assert sizes, padding, alignment, position in dp, or asks about `getUnclippedBoundsInRoot`, `DpRect`, touch-target size, or compares widths in pixels. If the developer is comparing layout dimensions from a test, use this skill. It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under Apache-2.
skydoves (@skydoves) published this skill. Their other Agent Skills are listed on their SkillMD profile.