{"id":42633360,"url":"https://github.com/timelessco/js-bottomsheet","last_synced_at":"2026-01-29T05:26:34.145Z","repository":{"id":63553797,"uuid":"539816397","full_name":"timelessco/js-bottomsheet","owner":"timelessco","description":"A pure Js snappable, scrollable, customisable bottomsheet!","archived":false,"fork":false,"pushed_at":"2025-04-28T06:10:28.000Z","size":22823,"stargazers_count":42,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-08T09:52:55.036Z","etag":null,"topics":["js","library","production","template","vanilla"],"latest_commit_sha":null,"homepage":"https://js-bottomsheet-website.vercel.app/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timelessco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-22T05:38:46.000Z","updated_at":"2025-10-06T13:17:23.000Z","dependencies_parsed_at":"2025-04-16T22:32:27.570Z","dependency_job_id":"d1ae296a-9f37-4f88-944b-d781fb427bf1","html_url":"https://github.com/timelessco/js-bottomsheet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/timelessco/js-bottomsheet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timelessco%2Fjs-bottomsheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timelessco%2Fjs-bottomsheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timelessco%2Fjs-bottomsheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timelessco%2Fjs-bottomsheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timelessco","download_url":"https://codeload.github.com/timelessco/js-bottomsheet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timelessco%2Fjs-bottomsheet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28863564,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T22:56:21.783Z","status":"online","status_checked_at":"2026-01-29T02:00:06.714Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["js","library","production","template","vanilla"],"created_at":"2026-01-29T05:26:34.062Z","updated_at":"2026-01-29T05:26:34.139Z","avatar_url":"https://github.com/timelessco.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Js Bottomsheet\n\n[![npm](https://badgen.net/npm/dt/js-bottomsheet)](https://www.npmjs.com/package/js-bottomsheet)\n\nA pure Js snappable, scrollable, customisable bottomsheet!\n\n## Features\n\n- Works as a bottomsheet in mobile and as a modal or sidesheet in web.\n- Supports multiple snap points.\n- Uses spring animations.\n- Supports stacking of bottomsheets.\n- Is scrollable at the last snappoint.\n- Supports dynamic content.\n- Sidesheets are resizable.\n\n## Dependencies:\n\n1. Use Gesture: https://use-gesture.netlify.app/docs/#vanilla-javascript\n2. Anime js: https://animejs.com/\n\n## Installation\n\nnpm:\n\n```\nnpm i js-bottomsheet\n```\n\n## Usage\n\nHere's a simple usage of the bottomsheet with static content.\n\nHTML:\n\n```\n  \u003cbutton id=\"trigger-1\" data-bottomsheet-id='bottomsheet-1'\u003eclick\u003c/button\u003e\n  \u003cdiv id=\"bottomsheet-1\" data-bottomsheet\u003eThe Bottomsheet\u003c/div\u003e\n```\n\nThe trigger should have an ID which would be used in the initialisation of the\nbottomsheet, and a data attribute containing the ID referring to it's\ncorresponding bottomsheet `data-bottomsheet-id=[id]`. The bottomsheet should\nhave the ID as well as the data attribute `data-bottomsheet`\n\nJS:\n\n```\nBottomSheet({\n  trigger: 'trigger-1',\n});\n```\n\nThis would create a simple bottomsheet with the default props.\n\n## Props:\n\nproperties that are common for all layouts - bottomsheet, modal and sidesheet\n\n### trigger:\n\nThe Id of the trigger element.\n\n\u003e Type: string\n\n### content:\n\nContent needn't always be a DOM element, it could be passed in dynamically too,\nas a string.\n\n\u003e Type: string\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n content: `\u003cdiv id=\"bottomsheet-1\" data-bottomsheet\u003eThe Bottomsheet\u003c/div\u003e`\n});\n```\n\n### headerContent:\n\nThe content that needs to remain fixed at the top of the sheet. It should have a\nclass of 'header'.\n\n\u003e Type: string\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n headerContent: `\u003cdiv class=\"header\"\u003e...\u003c/div\u003e`\n});\n```\n\n### footerContent:\n\nThe content that needs to remain fixed at the bottom of the sheet. It should\nhave a class of 'footer'.\n\n\u003e Type: string\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n footerContent: `\u003cdiv class=\"footer\"\u003e...\u003c/div\u003e`\n});\n```\n\n### displayOverlay:\n\nA boolean defining whether to display an overlay or not.\n\n\u003e Type: boolean\n\n\u003e Default: false\n\n### webLayout:\n\nThis property determines the behavior of the sheet in web view, which can either\nbe a modal or a sidesheet. Sidesheets can be configured to open from either the\nleft or the right side of the screen.\n\n\u003e Type: 'modal || sideSheetLeft || sideSheetRight'\n\n\u003e Default: 'modal'\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n content: \u003cdiv id=\"bottomsheet-1\" data-bottomsheet\u003eThe Bottomsheet\u003c/div\u003e,\n webLayout: 'sideSheetLeft'\n});\n```\n\n### openOnLoad:\n\nThis property enables the sheet to be automatically opened upon loading, without\nrequiring a trigger. Since there is no trigger, the content needs to be\ndynamically passed in.\n\n\u003e Type: boolean\n\n\u003e Default: false\n\neg:\n\n```\n BottomSheet({\n  content: `\u003cdiv id=\"maps-1\" data-bottomsheet\u003eThe sheet\u003c/div\u003e`,\n  openOnLoad: true,\n});\n```\n\n### cleanUpOnClose\n\nThis prop can be used to remove the sheet from the DOM on closing the sheet.\n\n\u003e Type: boolean\n\n\u003e Default: false\n\n### dismissible\n\nThis property determines whether the sheet can be closed by the user or not. If\nset to false, the sheet cannot be translated below it's minimum snap point.\n\n\u003e Type: boolean\n\n\u003e Default: true\n\n### closeOnOverlayClick\n\nA boolean to keep the sheet open or close it by clicking on the overlay.\n\n\u003e Type: boolean\n\n\u003e Default: true\n\n### scrollableSheet\n\nDefines whether the sheet is scrollable or not, when the sheet is at it's last\nsnap point.\n\n\u003e Type: booelean\n\n\u003e Default: true\n\n## Bottomsheet specific props\n\n### snapPoints:\n\nAn array of all the snappoints you'd like the bottomsheet to snap at, from\nbottom to top. Works as percentage.\n\nNote that mentioning 0 as the first snappoint will not open the sheet.\n\n\u003e Type: Array of numbers(from 1 to 100)\n\n\u003e Default: [100]\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n snappoints: [20, 60, 100]\n});\n```\n\n### draggableArea:\n\nAn area which would be sticky to the top of the sheet and would always listen to\ndrag events, even if the content of the sheet is scrollable. It should have an\nID.\n\n\u003e Type: string\n\neg:\n\n```\nBottomSheet({\n trigger: 'trigger-1',\n content: \u003cdiv id=\"bottomsheet-1\" data-bottomsheet\u003eThe Bottomsheet\u003c/div\u003e,\n draggableArea: `\u003cdiv id=\"drag\"\u003e\u003csvg  width=\"45\" height=\"7\" viewBox=\"0 0 45 7\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n \u003crect width=\"45\" height=\"7\" rx=\"3.5\" fill=\"#38383D\"/\u003e\u003c/svg\u003e\u003c/div\u003e`,\n});\n```\n\n### rubberband\n\nThe rubberband effect is only applicable if the sheet is at it's last snap point\nand is activated when scrollableSheet is set to false. It creates a snappy or\nbouncy behavior for the bottom sheet.\n\n\u003e Type: booelean\n\n\u003e Default: false\n\n### velocityThreshold\n\nThe velocity greater than which the bottomsheet would be translated to the next\nsnappoint, otherwise it would snap back to the old one.\n\n\u003e Type: number\n\n\u003e Default: 0.9\n\n### distanceThreshold\n\nThe distance greater than which the bottomsheet would be translated to the next\nsnappoint, otherwise it would snap back to the old one.\n\n\u003e Type: number\n\n\u003e Default: 150\n\n## Modal specific props\n\n### modalPosition\n\nIt's an array of x and y positions in numbers as percentage for the modal.\n\n\u003e Type: Array of numbers\n\n\u003e Default: [50, 50]\n\n### modalCloseIcon\n\nClose icon for the modal in web.\n\n\u003e Type: string\n\n\u003e Default:\n\n```\n`\u003csvg width=\"16\" height=\"12\" viewBox=\"0 0 39 38\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n \u003cpath d=\"M33.112 36.9133C34.3649 38.15 36.3963 38.15 37.6492 36.9133C38.9022 35.6766 38.9022 33.6716 37.6492 32.435L24.0375 19L37.6492 5.56496C38.9022 4.3283 38.9022 2.32328 37.6492 1.08662C36.3963 -0.150042 34.3649 -0.150042 33.112 1.08662L19.5002 14.5216L5.88835 1.08655C4.63541 -0.150108 2.60401 -0.150107 1.35108 1.08655C0.0981471 2.32321 0.0981459 4.32824 1.35108 5.5649L14.9629 19L1.35108 32.435C0.0981434 33.6717 0.0981443 35.6767 1.35108 36.9134C2.60401 38.15 4.63541 38.15 5.88834 36.9134L19.5002 23.4783L33.112 36.9133Z\" fill=\"white\"/\u003e\n \u003c/svg\u003e`\n```\n\n### minWidthForModal:\n\nThe minimum width at which you'd like a modal to appear.\n\n\u003e Type: number\n\n\u003e Default: 700\n\n## Sidesheet specific props:\n\n### sideSheetMinValue\n\nThe minimum snap point percentage at which the sidesheet would open, resizing\nbelow it would close the sheet.\n\n\u003e Type: number - 0 to 100\n\n### sideSheetMaxValue\n\nThe maximum snap point percentage upto which the sidesheet can be resized.\n\n\u003e Type: number - 0 to 100\n\n## Lifecycle methods\n\n### onInit\n\nThis callback function can be used to perform operations during the\ninitialization of the bottom sheet.\n\n\u003e Type: function\n\neg:\n\n```\nBottomSheet({\n      trigger: `target-1`,\n      onInit: () =\u003e {\n        console.log(\"init\");\n      }\n  });\n```\n\n### onOpen\n\nThis callback function can be used to perform operations once the sheet is open.\n\n\u003e Type: function\n\neg:\n\n```\nBottomSheet({\n      trigger: `target-1`,\n      onOpen: () =\u003e {\n        console.log(\"open\");\n      }\n    });\n  });\n```\n\n### onClose\n\nThis callback function can be used to perform operations once the sheet is\nclosed.\n\n\u003e Type: function\n\neg:\n\n```\nBottomSheet({\n      trigger: `target-1`,\n      onClose: () =\u003e {\n        console.log(\"close\");\n      }\n    });\n```\n\n### onDragStart\n\nThis callback function allows you to perform operations when starting to drag\nthe sheet.\n\n\u003e Type: function\n\neg:\n\n```\nBottomSheet({\n      trigger: `target-1`,\n      onDragStart: () =\u003e {\n        console.log(\"drag start\");\n      }\n    });\n```\n\n### onDragEnd\n\nOperations to be run when dragging of the sheet ends can be done using this\ncallback function.\n\n\u003e Type: function\n\neg:\n\n```\nBottomSheet({\n      trigger: `target-1`,\n      onDragEnd: () =\u003e {\n        console.log(\"drag end\");\n      }\n    });\n```\n\n## Methods\n\n### open\n\nCan be used to open the bottomsheet programatically. This works only if the\nsheet it already initialised.\n\neg:\n\n```\nlet bottomsheet1 = BottomSheet({\n      trigger: `target-1`\n  });  //this would open the sheet by default\n\n  bottomsheet1.open() //can be used if it's necessary to open the sheet in any other scenario.\n```\n\n### close\n\nCan be used to close the bottomsheet programatically. This works only if the\nsheet it already initialised and open.\n\neg:\n\n```\nlet bottomsheet1 = BottomSheet({\n      trigger: `target-1`\n  });\n\n  bottomsheet1.close()\n```\n\n### destroy\n\n\"When no longer needed, the event listeners can be removed from the triggers\nusing this method.\"\n\neg:\n\n```\nlet bottomsheet1 = BottomSheet({\n      trigger: `target-1`\n  });\n\n  bottomsheet1.destroy()\n```\n\n### init\n\nThe sheet can be re-initialised if needed, using this function.\n\neg:\n\n```\nlet bottomsheet1 = BottomSheet({\n      trigger: `target-1`\n  });\n\n  bottomsheet1.init()\n```\n\n## Styling\n\nYou can style the bottomsheet and other elements by overriding it's css styles.\n\n| Element          | ClassName     |\n| :--------------- | :------------ |\n| Bottomsheet      | 'bottomsheet' |\n| Overlay          | 'overlay'     |\n| Modal            | 'modal'       |\n| Modal Close Icon | 'close-modal' |\n| Draggable Area   | 'draggable'   |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimelessco%2Fjs-bottomsheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimelessco%2Fjs-bottomsheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimelessco%2Fjs-bottomsheet/lists"}