Repository navigation
Add ConnectorStyle.TEARDROP and ConnectorEndStyle.TEARDROP - #51
paul-at-equal wants to merge 1 commit into
Conversation
Adds a rounded teardrop/speech-bubble-tail shape as a new connector option, requested to match a reference design where the tooltip's tail is a smooth "hill" bump growing out of the card edge rather than a thin line with a dot/arrow. - ConnectorStyle.TEARDROP: a nub anchored directly on the tooltip card edge and pointing toward the target, with no connector line. - ConnectorEndStyle.TEARDROP: the same rounded shape used as a line endpoint decoration, as a smoother alternative to ARROW. - CoachmarkConfig.connectorTeardropLength / connectorTeardropWidth size the shape; connectorTeardropTargetGap controls the gap left between the nub's tip and the target so it doesn't touch it. - CoachmarkColors.connectorTeardropColor lets the shape's color be set independently of connectorColor (e.g. matched to the tooltip card background for a seamless attached-tail look). Also fixes a pre-existing off-by-one: isTooltipBelow used a strict `>` comparison, which flipped to the wrong tooltip side whenever the computed gap made the tooltip's edge land exactly on the target's edge (reachable via the new TEARDROP gap math, but a latent bug for any style). Bumped to 1.0.0-beta16 and updated CHANGELOG. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Before merging this, I want to flag something and ask you to re-scope rather than have it land as-is. The visual requirement is real — I checked it against the actual design (a speech-bubble tail on a coachmark tooltip, no connector line, tail colored to match the card so it reads as part of the card). Your But it's 2 new sealed enum cases + 4 new So I built and merged #53, which adds general extension points instead:
I verified on-device (Pixel 9 Pro Fold) that a teardrop identical to yours — rounded speech-bubble tail, no line, colored to match the card — is about 15 lines inside Ask: could you rebase this onto For reference, the extension-point PR is #53 (merged) — |
|
Update: I ended up taking a pass at this myself — #54 is the re-scoped version, ready for review. It reproduces the teardrop tail exactly (verified on-device, including matching it to the card color the way It also turned up two real bugs along the way (tail-direction inversion, and a pre-existing WCAG contrast failure in the dark-theme card colors) — both fixed and in #54's diff. No action needed from you unless you want to review #54 or have thoughts on the approach — happy to talk through it. Leaving this PR open for now rather than closing it outright, since it's your call whether to close it once #54 lands. |
…theme contrast fix (#54) * feat: per-target ConnectorStyle.CUSTOM, sample teardrop tail demo Dry-run for re-scoping #51 (ConnectorStyle.TEARDROP) onto #53's customConnector extension point. Finding: config.customConnector from #53 is sequence-wide only, with no per-target override. Paul's TEARDROP could mix a teardrop connector with an elbow connector in the same sequence (per-target CoachmarkTarget field); customConnector alone could not. This fixes that gap: - ConnectorStyle.CUSTOM: new enum case, the generic gate for full-control connector rendering (mirrors the existing ConnectorEndStyle.CUSTOM pattern). resolveConnectorStyle() resolves it through the same heuristic as AUTO for geometry purposes, so the anchors/fallback path stay correct. - CoachmarkTarget.customConnector: per-target override, takes priority over CoachmarkConfig.customConnector when both are set. - CUSTOM with no lambda supplied anywhere falls back to drawing a normal connector line rather than nothing, verified via a new screenshot test. Sample: added a "Teardrop Tail" step to ConnectorsExample using ConnectorStyle.CUSTOM, and a drawTeardropTail() helper — Paul's original "hill" silhouette (compared against a classic pointed teardrop and a true geometric pear/raindrop shape; the hill was the right call for a tail meant to read as continuous with a rounded card, not a separate sharp shape). Verified on-device end to end. Two new CoachmarkScrimScreenshotTest cases cover priority (target lambda wins over config default) and fallback (CUSTOM with nothing supplied still draws a line) behavior. * feat: speech-bubble tooltip shape with tail carved into the card outline Adds CoachmarkConfig.tooltipShape + tooltipTailInset: the tail is one continuous outline with the card (via a Shape), not a separately-drawn nub next to it — matches a real speech-bubble reference design where the tail reads as part of the card, no seam. - shapes/SpeechBubblePath.kt: SpeechBubbleShape + speechBubblePath(), following the existing StarPath/SquirclePath convention. The card body is sized to (measured size - tailInset) so the tail draws inside the Shape's own bounds rather than bumping past them, since clip() always bounds to the actual layout size. - CoachmarkTooltip gains tailAnchorX/tooltipShape/tooltipTailInset params. A Spacer inside the Column reserves tooltipTailInset on the tail's side so the Column's measured size includes room for the shape's tail. - Scrim computes tailAnchorX the same way the existing inline-dot-X code does: target center X relative to the tooltip's left edge. Sample: CustomRenderingExample's step 2 now uses tooltipShape instead of customConnector for the speech-bubble tail (customConnector kept only as a no-op to suppress the built-in line, since ConnectorStyle has no NONE case today - a real gap worth a follow-up). Two bugs found and fixed against on-device verification, not just visual inspection: 1. Tail direction was inverted (isTooltipBelow=true was mapped to tailOnBottom=true; it should be the opposite - tooltip below the target means the tail points UP, on the top edge, toward it). 2. tooltipCardColor in DarkCoachmarkColors and the default CoachmarkColors failed WCAG contrast against scrimColor: DarkCoachmarkColors measured 1.19:1, default measured 1.38:1 (need >=3:1 for a UI boundary), because both defaulted to a near-black card over a near-black scrim. The default's titleColor = Color.Black on a dark card additionally failed text contrast outright (1.38:1, needs >=4.5:1) - that one predates this change and was previously undetected because the sample never exercised showTooltipCard=true with the plain default colors. Fixed by moving both dark-mode-adjacent card colors to #666666 and fixing the default's title/description colors to white/near-white to match its (now-legible) dark card. Verified: card-vs-scrim >=3.1:1, title-vs-card >=6.2:1, description-vs-card >=3.86:1 in both palettes. LightCoachmarkColors was already fine (17.26:1) and is untouched. Re-recorded the 12 tooltip_* goldens affected by the card color change. Full suite green: 232 Android + 201 JVM tests, apiCheck passes. * feat(sample): speech-bubble tail on the LazyColumn whole-row target Adds CoachmarkConfig.tooltipShape to the LazyColumn example's existing "Item Cards" step (list_card), which already targets a whole row via CutoutShape.RoundedRect. This is the closest existing target to the reference speech-bubble tooltip design - a whole list row, tooltip above it, tail carved into the card's own bottom edge pointing down at the row, no connector line. Verified on-device against a real scrollable list. Also confirmed and documented a real gap while building this: tooltipShape only supports a top or bottom tail (SpeechBubbleShape has no left/right case), and CoachmarkScrim always computes tailAnchorX from target.bounds.center.x regardless of connector direction. Verified live with a temporary HORIZONTAL-connector target: the line correctly points sideways at the target while the tail sits meaninglessly on the bottom edge, disconnected from what the line points at. Left as a known limitation for a future PR rather than silently working around it - tooltipShape should not be combined with HORIZONTAL-style targets today. * chore: bump to 1.0.0-beta17 CHANGELOG entry for the tooltipShape / per-target CUSTOM connector / contrast-fix work in this branch.
|
Closing as superseded — #54 (merged) ships the same teardrop tail effect as a general extension point instead of new enum cases: Your original design call — the tail should be a filled hill/dome, not a sharp pointed teardrop — was correct and is what #54 uses; I compared it against a couple of alternatives (a classic concave-waist tail, a true geometric raindrop) before landing back on your original shape. No action needed on your end. Reopen this if you'd rather discuss the enum-based approach directly — happy to talk through it. |
Summary
ConnectorStyle.TEARDROP: a rounded teardrop/speech-bubble-tail nub that grows straight out of the tooltip card edge toward the target, with no connector line at all.ConnectorEndStyle.TEARDROP: the same rounded shape used as a connector line endpoint, as a smoother alternative toARROW.CoachmarkConfigknobs:connectorTeardropLength,connectorTeardropWidth(size the shape) andconnectorTeardropTargetGap(gap left between the nub's tip and the target).CoachmarkColors.connectorTeardropColorto color the teardrop shape independently ofconnectorColor— e.g. match it to the tooltip card background for a seamless attached-tail look.isTooltipBelow(>→>=) that flips the tooltip side when the computed gap makes the tooltip's edge land exactly on the target's edge — reachable via the new TEARDROP gap math, but a pre-existing edge case for any style.1.0.0-beta16, updatedCHANGELOG.mdand the API dump.Design notes
The teardrop shape is a smooth two-cubic-Bezier "hill" (flat base, single rounded peak) rather than the classic circle+point raindrop — this was iterated against a reference speech-bubble tail design and verified pixel-by-pixel against a Figma export (24×11 default proportions).
Test plan
./gradlew :lumen:verifyRoborazziDebug— 228/230 pass (2 pre-existing unrelated failures inCoachmarkTooltipScreenshotTest, not touched by this PR)./gradlew :lumen:jvmTest— all pass, including updatedConnectorStyle/ConnectorEndStyleentry-count assertions./gradlew :lumen:apiCheck— passes against the updated API dump🤖 Generated with Claude Code