RepoPilotOpen in app →

slackhq/PanModal

An elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.

Mixed

Slowing — last commit 6mo ago

worst of 4 axes
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isMixed

last commit was 6mo ago; no CI workflows detected

  • Last commit 6mo ago
  • 20 active contributors
  • Distributed ownership (top contributor 34% of recent commits)
Show 4 more →
  • MIT licensed
  • Tests present
  • Slowing — last commit 6mo ago
  • No CI workflows detected
What would change the summary?
  • Deploy as-is MixedHealthy if: 1 commit in the last 180 days

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Safe to depend on" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Safe to depend on
[![RepoPilot: Safe to depend on](https://repopilot.app/api/badge/slackhq/panmodal?axis=dependency)](https://repopilot.app/r/slackhq/panmodal)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/slackhq/panmodal on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: slackhq/PanModal

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/slackhq/PanModal shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Slowing — last commit 6mo ago

  • Last commit 6mo ago
  • 20 active contributors
  • Distributed ownership (top contributor 34% of recent commits)
  • MIT licensed
  • Tests present
  • ⚠ Slowing — last commit 6mo ago
  • ⚠ No CI workflows detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live slackhq/PanModal repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/slackhq/PanModal.

What it runs against: a local clone of slackhq/PanModal — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in slackhq/PanModal | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 218 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>slackhq/PanModal</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of slackhq/PanModal. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/slackhq/PanModal.git
#   cd PanModal
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of slackhq/PanModal and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "slackhq/PanModal(\\.git)?\\b" \\
  && ok "origin remote is slackhq/PanModal" \\
  || miss "origin remote is not slackhq/PanModal (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "PanModal/Presentable/PanModalPresentable.swift" \\
  && ok "PanModal/Presentable/PanModalPresentable.swift" \\
  || miss "missing critical file: PanModal/Presentable/PanModalPresentable.swift"
test -f "PanModal/Presenter/UIViewController+PanModalPresenter.swift" \\
  && ok "PanModal/Presenter/UIViewController+PanModalPresenter.swift" \\
  || miss "missing critical file: PanModal/Presenter/UIViewController+PanModalPresenter.swift"
test -f "PanModal/Delegate/PanModalPresentationDelegate.swift" \\
  && ok "PanModal/Delegate/PanModalPresentationDelegate.swift" \\
  || miss "missing critical file: PanModal/Delegate/PanModalPresentationDelegate.swift"
test -f "PanModal/Controller/PanModalPresentationController.swift" \\
  && ok "PanModal/Controller/PanModalPresentationController.swift" \\
  || miss "missing critical file: PanModal/Controller/PanModalPresentationController.swift"
test -f "PanModal/Animator/PanModalPresentationAnimator.swift" \\
  && ok "PanModal/Animator/PanModalPresentationAnimator.swift" \\
  || miss "missing critical file: PanModal/Animator/PanModalPresentationAnimator.swift"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 218 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~188d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/slackhq/PanModal"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

PanModal is a Swift framework that provides an elegant presentation API for iOS bottom sheet modals with seamless pan gesture handling. It enables view controllers to be presented as draggable, customizable bottom sheets while maintaining 60 fps performance and automatically managing pan gesture transitions between the modal and any embedded UIScrollView. Modular framework organized under PanModal/ with four core subsystems: Animator/ (PanModalAnimator.swift, PanModalPresentationAnimator.swift) handles transition animations; Controller/ (PanModalPresentationController.swift) manages the presentation lifecycle; Presentable/ (PanModalPresentable.swift + extensions) defines the protocol and helper methods; Presenter/ (UIViewController+PanModalPresenter.swift) provides the public API; View/ (DimmedView.swift, PanContainerView.swift) renders the visual components. Delegate/ provides PanModalPresentationDelegate for lifecycle hooks.

👥Who it's for

iOS developers at companies like Slack who need to present bottom sheet modals with thumb-friendly pan gestures and minimal boilerplate. Specifically designed for apps that want accessible, performant sheet presentations without complex custom transition code.

🌱Maturity & risk

Mature and production-ready. The framework has been actively used by Slack (as evidenced by their engineering blog post) and supports iOS 10+ with Swift 4.2+ compatibility. Version 1.2.6 is available via CocoaPods, Carthage, and SPM. Last visible activity shows ongoing maintenance with archived contributing guidelines, suggesting a stable, maintenance-focused state rather than rapid feature development.

Low risk for a small, focused library. The codebase is primarily Swift (67KB) with minimal dependencies (no external deps visible in file list), reducing dependency risk. Main risks are: single-maintainer (Slack org but no visible contributor diversity in docs), no test files visible in top 60 files suggesting limited test coverage, and iOS 10+ support requirement may be a blocker for newer iOS-only projects. No recent commit timestamps visible to assess abandonment risk.

Active areas of work

Repository appears in maintenance mode. The CONTRIBUTING-ARCHIVED.md filename suggests contributions are no longer actively solicited, and the codebase has stabilized around version 1.2.6. Focus is likely on bug fixes and compatibility rather than new features.

🚀Get running

git clone https://github.com/slackhq/PanModal.git
cd PanModal
pod install  # or use Carthage/SPM depending on preference
open PanModalDemo.xcodeproj

Daily commands: Open PanModalDemo.xcodeproj in Xcode and run the PanModalDemo scheme. The Sample/ directory contains AppDelegate.swift and example view controllers demonstrating presentPanModal() usage.

🗺️Map of the codebase

  • PanModal/Presentable/PanModalPresentable.swift — Core protocol defining the customization contract for any bottom sheet modal; all presentable objects must conform to this.
  • PanModal/Presenter/UIViewController+PanModalPresenter.swift — Extension enabling any UIViewController to present PanModal sheets; entry point for most integration.
  • PanModal/Delegate/PanModalPresentationDelegate.swift — Delegate protocol managing presentation lifecycle, transition callbacks, and gesture handling.
  • PanModal/Controller/PanModalPresentationController.swift — Custom UIPresentationController orchestrating layout, frame management, and dimmed view presentation.
  • PanModal/Animator/PanModalPresentationAnimator.swift — UIViewControllerAnimatedTransitioning implementation driving entrance/exit animations and pan gesture integration.
  • PanModal/View/PanContainerView.swift — Container view housing the modal content and handling pan gesture recognition and bounds tracking.
  • PanModal/Presentable/PanModalHeight.swift — Enum defining modal height strategies (intrinsic, maxHeight, static); fundamental to responsive layout.

🛠️How to make changes

Add a new bottom sheet modal type

  1. Create a new UIViewController subclass in Sample/View Controllers/{ModalName}/ (Sample/View Controllers/Basic/BasicViewController.swift)
  2. Ensure your ViewController conforms to PanModalPresentable protocol by implementing required computed properties (panModalHeight, panScrollable, shouldRoundTopCorners, etc.) (PanModal/Presentable/PanModalPresentable.swift)
  3. Use PanModalPresentable+Defaults.swift defaults for any properties you don't need to customize (most properties have sensible defaults). (PanModal/Presentable/PanModalPresentable+Defaults.swift)
  4. From a presenting UIViewController, call self.presentPanModal(YourViewController()) using the UIViewController+PanModalPresenter extension. (PanModal/Presenter/UIViewController+PanModalPresenter.swift)

Customize modal appearance and behavior

  1. Override properties in your conforming ViewController such as panModalHeight (using PanModalHeight enum: .intrinsic, .maxHeight, .static), backgroundColor, cornerRadius, etc. (PanModal/Presentable/PanModalHeight.swift)
  2. For scroll-linked behavior, set panScrollable to your UIScrollView/UITableView and implement shouldRespond(to:) to control gesture priority. (PanModal/Presentable/PanModalPresentable.swift)
  3. Implement optional delegate methods (allowsDragToDismiss, shouldTransitionTo, didTransitionTo) for lifecycle hooks if more advanced control is needed. (PanModal/Delegate/PanModalPresentationDelegate.swift)

Add conditional dismissal and gesture handling

  1. In your modal ViewController, implement the optional PanModalPresentationDelegate methods such as shouldRespond(to:), allowsDragToDismiss, or shouldTransitionTo(). (PanModal/Delegate/PanModalPresentationDelegate.swift)
  2. Use didTransitionTo() to detect when the user drags past the dismiss threshold and react accordingly (e.g., log analytics, prevent dismiss if form is dirty). (PanModal/Controller/PanModalPresentationController.swift)
  3. For advanced pan gesture customization, inspect the PanContainerView which owns the gesture recognizer; modify pan responsiveness via shouldRespond(to:) or allowsDragToDismiss. (PanModal/View/PanContainerView.swift)

Integrate responsive height (intrinsic sizing)

  1. Set panModalHeight to .intrinsic in your ViewController's conformance to PanModalPresentable. (PanModal/Presentable/PanModalHeight.swift)
  2. The presentation controller will call systemLayoutSizeFitting() on your view to measure its preferred height; ensure your view controller's view uses AutoLayout with top-to-bottom constraints. (PanModal/Controller/PanModalPresentationController.swift)
  3. Use PanModalPresentable+LayoutHelpers.swift helpers (panModalHeight, topMargin, etc.) to align your layout with the framework's safe area and margin conventions. (PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift)

🔧Why these technologies

  • UIViewControllerTransitioningDelegate + UIPresentationController — iOS standard for custom presentation animations; enables full control over frame, timing, and gesture integration without reimplementing dismissal logic.
  • UIPanGestureRecognizer with interactive UIViewPropertyAnimator — Provides fluid, interruptible drag-to-dismiss; users can reverse a dismiss gesture mid-flight, matching native iOS patterns.
  • Protocol-based conformance (PanModalPresentable) — Any UIViewController can adopt via extension; no subclassing required, minimizing coupling and enabling incremental adoption.
  • AutoLayout + systemLayoutSizeFitting() for intrinsic height — Decouples modal height from hardcoded values; sheets adapt to content automatically, supporting dynamic text and nested scrolling.

⚖️Trade-offs already made

  • Protocol-based design with per-instance customization via computed properties rather than configuration objects

    • Why: Reduces object allocation and aligns with UIViewController lifecycle; each property is lazily evaluated.
    • Consequence: Properties must be deterministic at presentation time; dynamic runtime changes to e.g. panModalHeight after presentation may not re-layout correctly.
  • Pan gesture owned by PanContainerView rather than presentation controller

    • Why: Isolates gesture logic to the view hierarchy; allows modal content to have its own gesture recognizers (e.g., UIScrollView) without conflicts.
    • Consequence: Gesture coordination is implicit (shouldRespond callback); complex multi-gesture scenarios require careful implementation of shouldRespond(to:).
  • Synchronous height measurement via systemLayoutSizeFitting() for .intrinsic mode

    • Why:
    • Consequence: undefined

🪤Traps & gotchas

No test files visible in top 60—unclear what test coverage exists or how to run tests before submitting changes. The gesture bridging between modal and UIScrollView (mentioned in README) has complex state management in PanContainerView; modifying it without understanding the full pan gesture flow could break seamless transitions. iOS 10+ requirement means many modern features (safe area, notch handling) require careful abstraction. No visible CI/GitHub Actions configuration suggests manual testing burden for contributions.

🏗️Architecture

💡Concepts to learn

  • UIPresentationController — PanModal's entire presentation lifecycle (sizing, dismissal, animation delegation) is built on top of the custom presentation controller pattern; understanding this is essential to modifying layout or animation behavior
  • Gesture Bridging / Hit Testing — The core innovation of PanModal is seamlessly forwarding pan gestures from the modal to embedded UIScrollViews; this requires careful hit testing and gesture recognizer coordination that's non-obvious
  • Interactive Transition Controller — PanModal uses UIViewControllerInteractiveTransitioning implicitly through custom gesture handling to create the interactive dismiss animation; understanding this pattern is crucial for modifying drag-to-dismiss behavior
  • Safe Area Layout Insets — Bottom sheet modals must respect safe area (notches, home indicators) for proper positioning; PanModal handles this implicitly but customization requires understanding safe area constraints
  • CADisplayLink / Frame Pacing — PanModal claims 60 fps performance; achieving this during pan gesture animation requires understanding frame-rate-aware animation and potential use of CADisplayLink for gesture tracking
  • Protocol-Oriented Design (Swift) — PanModalPresentable is a protocol with default implementations across four extension files (.swift+Defaults, +LayoutHelpers, +UIViewController); understanding composition via protocol extensions is essential for customization
  • hackiftekhar/IQKeyboardManager — Complementary iOS keyboard management library often paired with bottom sheet modals to handle keyboard appearance during text input
  • jrendel/SwiftKeychainWrapper — Common companion for modal-based authentication flows that PanModal supports
  • airbnb/lottie-ios — Animation library frequently used with PanModal to enhance transition and dismissal animations beyond default UIView timing
  • raushankit/SimilarWebAPI-iOS — Example of a modal-heavy iOS app architecture that could benefit from PanModal's presentation pattern

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for PanModalPresentable protocol and its extensions

The repo contains multiple PanModalPresentable extension files (PanModalPresentable.swift, PanModalPresentable+Defaults.swift, PanModalPresentable+LayoutHelpers.swift, PanModalPresentable+UIViewController.swift) but there's no visible test directory. These core protocol implementations need unit tests to ensure height calculations, layout helpers, and default behaviors work correctly across different view controller configurations.

  • [ ] Create Tests/ directory structure with PanModalPresentableTests.swift
  • [ ] Add tests for PanModalHeight enum cases (intrinsic, maxHeight, custom values) in PanModalPresentable+Defaults.swift
  • [ ] Add tests for layout helper calculations in PanModalPresentable+LayoutHelpers.swift
  • [ ] Add tests for UIViewController extension methods in PanModalPresentable+UIViewController.swift
  • [ ] Test edge cases like safe area insets, landscape orientation, and various device sizes

Add GitHub Actions CI/CD workflow for building and testing across iOS versions

The repo targets iOS 10+ and Swift 5, but there's no visible CI configuration. Adding a GitHub Actions workflow would ensure pull requests are validated against multiple iOS deployment targets, preventing regressions in the animation system (PanModalAnimator.swift, PanModalPresentationAnimator.swift) and presentation controller logic.

  • [ ] Create .github/workflows/build-test.yml with xcodebuild steps
  • [ ] Configure matrix testing for iOS 13, 14, 15, 16 minimum deployments
  • [ ] Add steps to build both PanModalDemo.xcodeproj and validate Package.swift
  • [ ] Run any existing tests and generate coverage reports
  • [ ] Add workflow badge to README.md

Document the PanModalPresentationController and gesture handling flow with code examples

PanModalPresentationController.swift is a core file but lacks specific documentation about how pan gesture recognition, dismissal thresholds, and animation state management work. The Sample app shows usage but there's no detailed guide in documentation explaining the internal flow that new contributors need to understand when modifying gesture or animation behavior.

  • [ ] Create ARCHITECTURE.md documenting PanModalPresentationController's role in the presentation lifecycle
  • [ ] Add inline code documentation to key methods in PanModalPresentationController.swift explaining gesture handling logic
  • [ ] Document the interaction between PanModalAnimator, PanModalPresentationAnimator, and PanModalPresenter
  • [ ] Add a sequence diagram or flowchart showing dismiss vs. present animation states
  • [ ] Include example code snippets showing common customization patterns referenced in CONTRIBUTING.md

🌿Good first issues

  • Add unit tests for PanModalHeight.swift to validate height calculation logic across all .intrinsic, .maxAvailable, and fixed CGFloat cases—currently no test files visible
  • Document the PanModalPresentable protocol in PanModal/Presentable/PanModalPresentable.swift with inline code examples showing how to implement each optional method (panScrollable, shortFormHeight, longFormHeight, etc.) to make onboarding easier
  • Add a commented example in Sample/ showing how to handle dynamic height changes (e.g., keyboard appearance) by implementing PanModalPresentable methods and calling panModalTransition(to:animated:) from the presenter

Top contributors

Click to expand
  • [@Stephen Sowole](https://github.com/Stephen Sowole) — 30 commits
  • [@Tosin Afolabi](https://github.com/Tosin Afolabi) — 24 commits
  • @tun57 — 9 commits
  • @Marcocanc — 7 commits
  • @nikitskynikita — 2 commits

📝Recent commits

Click to expand
  • 76ab89f — This repo is ARCHIVED (sfdc-ospo-bot)
  • b2f5bd7 — Add completion block to present function (#94) (jordanpichler)
  • b012aec — bump version number. (Tosin Afolabi)
  • 22b4ddd — [PanModal] Track all dismiss events through panModalPresentable callbacks (#91) (Stephen Sowole)
  • 17a8231 — Update README.md (Stephen Sowole)
  • 1819113 — [ReadMe] Add link to Swift Package Manager (Stephen Sowole)
  • 208640e — [PanModal] Round frame positions for comparison (#83) (Stephen Sowole)
  • f02439d — [PanModal] Fix issue with incorrect returned bottom offset value (#84) (Stephen Sowole)
  • 0474150 — Fix Background dimming animation broken (#77) (KyoheiG3)
  • 1e7c053 — Use keyPath to read UIApplication.shared. (#81) (abdullahselek)

🔒Security observations

PanModal is a presentation library with a generally secure architecture. No critical vulnerabilities were identified in the static analysis. The main security considerations are administrative in nature: the project should establish clearer security policies, increase the minimum iOS deployment target from the outdated iOS 10, and create a vulnerability disclosure mechanism. As a UIKit wrapper library, it has minimal dependency and injection attack surface. The codebase follows Swift best practices with no apparent hardcoded secrets, SQL injection vectors, or exposed credentials. Regular dependency audits and timely security updates for supported iOS versions would further strengthen the security posture.

  • Low · Missing Code Signing Configuration Documentation — PanModal/Info.plist, PanModalDemo.xcodeproj/project.pbxproj. The project contains iOS app configuration files (.plist, .xcodeproj) but no explicit documentation on code signing requirements or best practices for secure distribution. This could lead to developers inadvertently signing apps with incorrect certificates. Fix: Add code signing guidelines to CONTRIBUTING.md and document required provisioning profiles and certificate requirements.
  • Low · No SECURITY.md File — Repository root. The repository lacks a SECURITY.md file to communicate security policies, vulnerability disclosure procedures, and supported versions. This makes it difficult for security researchers to responsibly report vulnerabilities. Fix: Create a SECURITY.md file following GitHub's recommended format to establish a clear vulnerability disclosure policy.
  • Low · Outdated iOS Minimum Deployment Target — README.md, Package.swift, PanModal.podspec. The project supports iOS 10+ according to the README badge. iOS 10 reached end-of-life in 2017 and no longer receives security updates. Supporting such old versions increases the attack surface. Fix: Increase the minimum iOS deployment target to iOS 12 or later (preferably 13+) to ensure security patches are available for all supported platforms.
  • Low · No Dependency Pinning in Package.swift — Package.swift. Without visibility into Package.swift content, there is a risk of transitive dependency vulnerabilities if version constraints are too permissive (e.g., using '^' or '*' for all dependencies). Fix: Review and pin dependencies to specific versions or narrow version ranges. Regularly audit dependencies using tools like 'swift package update --dry-run' and check security advisories.
  • Low · Sample Application May Expose Debug Information — Sample/ directory, Sample/AppDelegate.swift. The PanModalDemo sample application is included in the repository structure. If built and distributed with debug symbols or logging enabled, it could expose sensitive information. Fix: Ensure sample app builds do not include debug symbols or verbose logging in release configurations. Use preprocessor directives (#if DEBUG) to control debug output.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.