Skip to content

feat(a11y/ui): profile-bio auto-collapse, mobile bottom-sheet with drag-to-dismiss (#315/#314)#325

Merged
Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
DeJune06:feat/profile-bio-collapse-bottom-sheet-drag
May 28, 2026
Merged

feat(a11y/ui): profile-bio auto-collapse, mobile bottom-sheet with drag-to-dismiss (#315/#314)#325
Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
DeJune06:feat/profile-bio-collapse-bottom-sheet-drag

Conversation

@DeJune06
Copy link
Copy Markdown

@DeJune06 DeJune06 commented May 28, 2026

This PR closes #315 and #314 — two related profile/mobile UX issues in the creator marketplace.

closes #315
closes #314

What's in this PR

#315 — Auto-collapse + expand toggle for the creator profile bio

  • CreatorBio gains three opt-in props:
    • collapsible: boolean — turns the feature on. Only meaningful for the profile variant; the card variant ignores it (the card already clamps via maxLines).
    • collapsedMaxLines: number (default 4) — line count in the collapsed state.
    • collapseThresholdChars: number (default 200) — character-count threshold above which the toggle appears. Short bios are unaffected (no toggle, no clamp).
  • When engaged, the bio renders clamped with a focusable <button> ("Show more" / "Show less") that carries aria-expanded and aria-controls={bio paragraph id} so screen readers communicate the collapsed/expanded state and link the toggle to the content it controls.
  • Wired into CreatorProfileHeader where the profile bio is actually rendered.

#314 — Drag-to-dismiss bottom sheet primitive

  • New src/components/ui/bottom-sheet.tsx exporting BottomSheet, BottomSheetContent, BottomSheetHandle, BottomSheetClose, BottomSheetTitle, BottomSheetDescription.
  • Built on @radix-ui/react-dialog to inherit the focus trap, role="dialog", Escape handling, and Portal/overlay layering the rest of the app already relies on.
  • Native pointer events drive the gesture — no new gesture library pulled in.
  • Gesture acceptance criteria:
    • A downward drag of at least dismissThresholdPx (default 96px) dismisses the sheet by dispatching Escape, so Radix's onOpenChange(false) pipeline runs and the rest of the app's cleanup behaves identically to keyboard dismissal.
    • Upward drag is clamped at 0 — we don't expose pull-up.
    • The drag handle (<BottomSheetHandle />) registers itself with the content surface so a gesture starting on the handle is always captured.
    • Otherwise the gesture is only captured when no inner scroller is engaged: we walk up from the pointer target, and if any ancestor has scrollTop > 0, the gesture is left to that scroller (no accidental "trying to scroll up = dismiss the sheet" misfire).
    • enableDrag={false} disables the gesture entirely, leaving the close button as the only dismissal path.
    • The default close button (hideCloseButton={false}) always works — that's the primary accessible dismissal affordance for keyboard / screen-reader users.

Tests

12 new vitest cases, all passing:

  • src/components/common/__tests__/CreatorBio.test.tsx (6 added in the #315 describe block): no toggle for short bio, no engagement on the card variant, clamp + toggle wiring (aria-expanded/aria-controls), toggle to "Show less" + clamp removal on expand, custom collapseThresholdChars, custom collapsedMaxLines.
  • src/components/ui/bottom-sheet.test.tsx (6 cases): render (handle + close button + title), close-button dismissal, drag past threshold dismisses, short drag does not, upward drag clamps and never dismisses, enableDrag={false} leaves close button as only path.
$ pnpm install        # ok
$ pnpm test           # 129 passed | 1 failed (CreatorInitialsAvatar, pre-existing)
$ pnpm lint           # clean
$ pnpm build          # clean (vite build, 5.86s)

Disclosures

  1. src/components/common/__tests__/CreatorInitialsAvatar.test.tsx fails on main — same pre-existing failure disclosed in PRs feat(a11y/ui): main landmark, price-refresh state, no-script fallback, handle norm (#306/#305/#299/#298) #310 (kalveen), feat(a11y/ui): profile header skeleton, accessible metric tooltips, bio clamp, onboarding placeholders (#291/#290/#282/#273) #311 (precious), and feat(a11y/ux): high-contrast overlay tokens, stale-data warning, staggered card entry (#313/#301/#300) #324 (mawuli). The test queries getByLabelText('Alex Rivers initials avatar'), but the component renders the initials with aria-hidden="true" (no matching aria-label). Not touched here.
  2. No consumer wired up for BottomSheet yet. The repo's existing mobile dismissal flows go through Radix Dialog directly; the issue's wording ("identify the bottom sheet panel components used on mobile") doesn't match any existing component. Rather than re-skin an unrelated TradeDialog into a bottom sheet, this PR ships the primitive + tests so a follow-up can adopt it as the platform pattern. The tests exercise the gesture surface end-to-end, so the primitive is verified independently of any consumer.

…with drag-to-dismiss

Closes accesslayerorg#315, accesslayerorg#314.

- accesslayerorg#315 CreatorBio: gains collapsible + collapsedMaxLines +
  collapseThresholdChars props. When enabled on the profile variant
  and the bio is long enough (default >200 chars), the paragraph
  renders clamped with a focusable Show more / Show less toggle that
  carries aria-expanded + aria-controls so screen readers know the
  bio's collapsed state. Short bios are unaffected (no toggle, no
  clamp). The card variant ignores collapsible since it already
  clamps via maxLines. Wired into CreatorProfileHeader where the
  profile bio actually renders.

- accesslayerorg#314 BottomSheet: new mobile-first primitive built on Radix Dialog
  for the focus trap / role / Escape handling. Adds drag-to-dismiss
  via native pointer events:
    * The visual handle (BottomSheetHandle) registers itself with the
      sheet's content surface so a gesture that starts on the handle
      is always treated as 'grabbing the sheet'.
    * Otherwise the gesture is captured only when no inner scroller
      is engaged (walks up from the target, bails if any ancestor
      has scrollTop>0) — so a downward swipe on scrollable content
      scrolls instead of dismissing.
    * Dragging past dismissThresholdPx (default 96) dismisses by
      dispatching Escape so Radix's onOpenChange(false) pipeline runs.
    * Short / upward drags snap back via a brief transform reset.
    * The default close button always works as an alternative; pass
      enableDrag=false to make it the only path.

Tests (12 new, all passing):
- src/components/common/__tests__/CreatorBio.test.tsx (6 new cases):
  no toggle for short bio, no engagement on card variant, clamps +
  Show more wiring, toggles to Show less + removes clamp, custom
  collapseThresholdChars, custom collapsedMaxLines.
- src/components/ui/bottom-sheet.test.tsx (6 cases): render +
  handle + close button, close-button dismissal, drag past threshold
  dismisses, short drag does not, upward drag clamps + never
  dismisses, enableDrag=false leaves close button as only path.

Repo verification:
- pnpm test    129/130 (1 pre-existing CreatorInitialsAvatar failure,
                same one disclosed in accesslayerorg#310 / accesslayerorg#311 / accesslayerorg#324).
- pnpm lint    clean
- pnpm build   clean

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 28, 2026

@DeJune06 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Chucks1093 Chucks1093 merged commit 8dc6307 into accesslayerorg:main May 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add auto-collapse behavior for creator profile bio with expand toggle Add drag-to-dismiss support for mobile bottom sheet panels

2 participants