skiptools / skiptools/skip-ui

Reorder drag has no visible affordance on dark backgrounds: dragging flag is spent on an invisible shadow

Open
#517 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Swift
Stars
330
Forks
76
Avg merge
3h 6m
Merged PRs (30d)
1

Description

Summary

List computes whether a row is being dragged and spends it entirely on
Modifier.shadow(8.dp). On a dark app that shadow is invisible, so a reorder has
no visible affordance at all — the row you are dragging looks identical to
every other row. There is no way to reach the flag from Swift to supply your own
treatment.

Where

Sources/SkipUI/SkipUI/Containers/List.swift, RenderReorderableItem:

@Composable private func RenderReorderableItem(reorderableState: ReorderableLazyListState, key: String, modifier: Modifier, content: @Composable (Modifier) -> Void) {
    ReorderableItem(state: reorderableState, key: key, defaultDraggingModifier: modifier) { dragging in
        var itemModifier = Modifier.detectReorderAfterLongPress(reorderableState)
        if dragging {
            let elevation = animateDpAsState(8.dp)
            itemModifier = itemModifier.shadow(elevation.value)
        }
        content(itemModifier)
    }
}

dragging is known here and discarded. A shadow communicates elevation by
darkening what is around the row, which does nothing over #000/#141414.
Material's own answer on dark surfaces is tonal elevation — the surface gets
lighter — which a shadow cannot express.

Why an app cannot work around it

  • The flag never reaches Swift. onMove is invoked from onDragEnd
    (rememberReorderableLazyListState(..., onDragEnd:)commitMove()), so
    SwiftUI's .onMove fires on drop. During the drag the app knows nothing.
  • A gesture of one's own does not fire. Modifier.detectReorderAfterLongPress
    claims the long press first, so .onLongPressGesture(..., onPressingChanged:)
    on the row never sees the press. Verified on device.
  • And that callback is one-way regardless. onLongPressChange() calls
    onChanged(true); nothing ever calls it with false
    (System/Gesture.swift), so a press-derived highlight would latch on rather
    than follow the finger.

Suggested directions

  1. Apply a tonal treatment as well as the shadow when dragging — e.g. compose the
    row over a surface tinted by elevation, so dark themes get feedback that a
    shadow cannot give them.
  2. Or make it configurable, in the shape material3Ripple already uses: an
    environment hook letting the app supply the dragging modifier.
  3. Or expose dragging to the row content so the app can style it, which is what
    SwiftUI apps do on iOS anyway.

Any one of these turns a currently-invisible interaction into a visible one. (1)
alone would fix it for every dark-themed Skip app without an API change.

Environment

skip-ui as vendored in a Skip app, Android 14, Samsung SM-A326U. Reproduces with
any List using .onMove on a dark background.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Sources/SkipUI/SkipUI/Containers/List.swift at RenderReorderableItem and trace how dragging is supplied by ReorderableItem and how onDragEnd commits the move. Review System/Gesture.swift for the documented gesture callback behavior, then reproduce List reordering on a dark background. Done means the active row has a visible drag affordance without changing drop behavior, with the chosen treatment or API direction clearly settled.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, swift
Domain
design, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.