Repository navigation
feat: speech-bubble tooltip shape, per-target CUSTOM connector, dark-theme contrast fix - #54
Merged
Merged
Conversation
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.
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.
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.
CHANGELOG entry for the tooltipShape / per-target CUSTOM connector / contrast-fix work in this branch.
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Re-scopes #51 (
ConnectorStyle.TEARDROP) onto #53's extension points, and closes the real gap that surfaced while doing it: a true speech-bubble tooltip shape, not a separately-drawn nub.CoachmarkConfig.tooltipShape+io.luminos.shapes.SpeechBubbleShape: the tail is carved into the tooltip card's ownShapeoutline — one continuousPath, no seam, follows the card's own background — instead of a connector-drawn nub sitting next to the card.CoachmarkConfig.tooltipTailInsetreserves the extra layout space the tail needs.ConnectorStyle.CUSTOM+CoachmarkTarget.customConnector: a per-target override on top of feat: composable/lambda extension points for tooltip, connector, and cutout rendering #53's sequence-wideCoachmarkConfig.customConnector, so one step in a sequence can differ from the rest (mirrors the existingConnectorEndStyle.CUSTOMpattern).CUSTOMwith no lambda anywhere falls back to a normal line rather than nothing.isTooltipBelow=trueincorrectly mapped to a bottom-edge tail).DarkCoachmarkColors/defaultCoachmarkColors()failed WCAG contrast:tooltipCardColorvsscrimColormeasured 1.19:1–1.38:1 (need ≥3:1). The default'stitleColor = Color.Blackon its own dark card additionally failed text contrast outright (1.38:1, needs ≥4.5:1) — pre-existing, unrelated to this branch, previously undetected because no sample exercisedshowTooltipCard = truewith plain defaults. Fixed both to#666666+ matching light text; verified ≥3.1:1 card-vs-scrim, ≥6.2:1 text-vs-card.LightCoachmarkColorsuntouched (was already 17.26:1).Known limitation (documented, not silently worked around)
tooltipShape/SpeechBubbleShapeonly support a tail on the top or bottom edge. Verified live with a temporaryHORIZONTAL-connector target: the line correctly points sideways while the tail sits on the bottom edge, unrelated to where the line points. Don't combinetooltipShapewith side-positioned targets yet — left as a follow-up.Sample
CustomRenderingExample: "Speech Bubble Tail" step replaces the earlier connector-nub demo.LazyColumnExample: "Item Cards" step (a real whole-row target) now usestooltipShape, matching a reference speech-bubble tooltip design end-to-end.Test plan
./gradlew :lumen:apiCheck— passes, dumps regenerated./gradlew :lumen:testDebugUnitTest :lumen:jvmTest— 232 + 201 tests, 0 failures./gradlew :lumen:compileKotlinMetadata :lumen:compileDebugKotlinAndroid :lumen:compileKotlinJvm :sample:compileDebugKotlinAndroid :sample:compileKotlinWasmJs— all green1.0.0-beta17, CHANGELOG updated