[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"quiz-css-scroll-snap-native-carousel":44,"search-suggestions":60,"quiz-article-css-scroll-snap-native-carousel":105},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"kind":47,"title":48,"description":49,"config":50,"verticalId":5,"vertical":55,"course":52,"_count":56,"access":57,"attempts":59,"questionCount":51},"019fff35-496f-76d3-9073-a8f11d261368","css-scroll-snap-native-carousel","PRACTICE_QUIZ","CSS scroll-snap","Eight questions on what scroll-snap-type and scroll-snap-align actually control, the mandatory\u002Fproximity split, scroll-padding vs. scroll-margin, and the one thing CSS still can't do for a carousel.",{"questionCount":51,"timeLimitSec":52,"shuffleQuestions":18,"shuffleOptions":17,"negativeMarking":19,"passScorePct":53,"maxAttempts":52,"revealAnswers":54,"allowFlagging":18,"allowBacktracking":17},8,null,70,"AFTER_SUBMIT",{"slug":6,"name":7},{"questions":51},{"allowed":17,"reason":58},"FREE",[],[61,65,69,73,77,81,84,88,92,96,99,102],{"slug":62,"name":63,"articles":64},"webdev","Webdev",57,{"slug":66,"name":67,"articles":68},"frontend","Frontend",51,{"slug":70,"name":71,"articles":72},"javascript","Javascript",50,{"slug":74,"name":75,"articles":76},"css","Css",22,{"slug":78,"name":79,"articles":80},"typescript","Typescript",10,{"slug":82,"name":83,"articles":51},"performance","Performance",{"slug":85,"name":86,"articles":87},"grammar","Grammar",6,{"slug":89,"name":90,"articles":91},"react","React",5,{"slug":93,"name":94,"articles":95},"tutorial","Tutorial",4,{"slug":97,"name":98,"articles":95},"node","Node",{"slug":100,"name":101,"articles":43},"ai","Ai",{"slug":103,"name":104,"articles":43},"html","Html",{"id":106,"slug":46,"title":107,"subtitle":52,"excerpt":108,"coverUrl":109,"locale":13,"readingMinutes":95,"publishedAt":110,"viewCount":53,"likeCount":19,"commentCount":19,"author":111,"vertical":116,"topic":117,"tags":119,"_count":124,"playground":126,"body":128,"bodyMd":283,"seo":284,"translationGroupId":286,"thread":287,"assessments":289,"translations":292,"quiz":294},"019fff35-4908-727e-b7a3-dafd1ada4784","I Ripped Out a Carousel Library. CSS Replaced It.","A scroll listener, a debounce, and 200 lines of carousel-library glue code — replaced by three CSS properties that snap smoother than any of it did.","\u002Fmedia\u002Fcovers\u002Fcss-scroll-snap-native-carousel.png","2026-08-18T06:50:52.267Z",{"id":112,"name":113,"username":114,"avatarUrl":52,"headline":115},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":74,"name":118},"CSS",[120,121,122,123],{"slug":74,"name":75,"color":52},{"slug":62,"name":63,"color":52},{"slug":66,"name":67,"color":52},{"slug":93,"name":94,"color":52},{"assessments":125},1,{"slug":46,"title":127},"CSS scroll-snap — interactive playground",{"blocks":129,"version":125},[130,134,137,140,144,147,153,156,159,163,166,169,173,176,180,183,186,189,192,195,198,201,204,207,213,216,220,223,226,230,233,236,239,242,245,248,251,254,257,260,263,266,269,272,275],{"id":131,"html":132,"type":133},"b1","\u003Cp>The bug ticket said &quot;carousel feels broken on trackpad.&quot; It took me forty minutes to find the actual cause: my carousel library&#39;s \u003Ccode>scrollTo\u003C\u002Fcode> animation and the browser&#39;s own momentum scrolling were both trying to move the same element at the same time, and they disagreed.\u003C\u002Fp>","paragraph",{"id":135,"html":136,"type":133},"b2","\u003Cp>Two \u003Ccode>requestAnimationFrame\u003C\u002Fcode> loops, fighting over one \u003Ccode>scrollLeft\u003C\u002Fcode> value, several times a second.\u003C\u002Fp>",{"id":138,"html":139,"type":133},"b3","\u003Cp>I fixed it by deleting the library.\u003C\u002Fp>",{"id":141,"html":142,"text":142,"type":143,"level":31},"b4","The carousel everyone hand-rolls","heading",{"id":145,"html":146,"type":133},"b5","\u003Cp>Here&#39;s the pattern if you haven&#39;t built one recently: a flex row of cards, \u003Ccode>overflow-x: auto\u003C\u002Fcode>, and then JavaScript to make it feel like a carousel instead of a scrollbar with cards in it.\u003C\u002Fp>",{"id":148,"code":149,"type":150,"language":151,"highlight":152},"b6","function scrollToCard(index) {\n  const card = cards[index];\n  track.scrollTo({ left: card.offsetLeft, behavior: \"smooth\" });\n}\n\ntrack.addEventListener(\"scroll\", debounce(() => {\n  const nearest = findNearestCard(track.scrollLeft);\n  setActiveDot(nearest);\n}, 100));\n\nnextBtn.addEventListener(\"click\", () => scrollToCard(activeIndex + 1));","code","js",[],{"id":154,"html":155,"type":133},"b7","\u003Cp>A \u003Ccode>scroll\u003C\u002Fcode> listener to track position. A debounce so it doesn&#39;t fire on every pixel. A \u003Ccode>findNearestCard\u003C\u002Fcode> function doing \u003Ccode>offsetLeft\u003C\u002Fcode> math. And the moment a user drags with a finger or a trackpad instead of clicking your arrows, you&#39;re racing the browser&#39;s native scroll physics with your own \u003Ccode>scrollTo\u003C\u002Fcode> calls — which is exactly the bug that ate my Tuesday.\u003C\u002Fp>",{"id":157,"html":158,"type":133},"b8","\u003Cp>None of that code is \u003Cem>wrong\u003C\u002Fem>, exactly. It&#39;s just solving a problem the browser already solved, badly enough that a plugin market exists around it.\u003C\u002Fp>",{"id":160,"html":161,"text":162,"type":143,"level":31},"b9","What &quot;snap&quot; actually means to the browser","What \"snap\" actually means to the browser",{"id":164,"html":165,"type":133},"b10","\u003Cp>CSS has had scroll snapping since 2019 — Baseline, in every current browser, no vendor prefix, no polyfill. It doesn&#39;t animate a scroll position for you. It changes where the browser lets a scroll gesture \u003Cem>rest\u003C\u002Fem>.\u003C\u002Fp>",{"id":167,"html":168,"type":133},"b11","\u003Cp>Two properties do the whole job. One goes on the scroll container:\u003C\u002Fp>",{"id":170,"code":171,"type":150,"language":74,"highlight":172},"b12",".track {\n  display: flex;\n  overflow-x: auto;\n  scroll-snap-type: x mandatory;\n}",[],{"id":174,"html":175,"type":133},"b13","\u003Cp>One goes on each card:\u003C\u002Fp>",{"id":177,"code":178,"type":150,"language":74,"highlight":179},"b14",".card {\n  scroll-snap-align: center;\n}",[],{"id":181,"html":182,"type":133},"b15","\u003Cp>That&#39;s it. Drag, flick, trackpad-swipe, or scrollbar-drag the track, and the browser&#39;s own scroll physics — the momentum, the deceleration curve, the bit that feels \u003Cem>right\u003C\u002Fem> on every input device because the browser&#39;s engineers tuned it for every input device — settles on whichever card&#39;s snap point is closest. No \u003Ccode>scrollTo\u003C\u002Fcode>, no debounce, no \u003Ccode>findNearestCard\u003C\u002Fcode>.\u003C\u002Fp>",{"id":184,"html":185,"type":133},"b16","\u003Cp>Try dragging it yourself before you take the rest of my word for it.\u003C\u002Fp>",{"id":187,"html":188,"type":133},"b17","\u003C!-- playground:start -->",{"id":190,"html":191,"text":191,"type":143,"level":31},"b18","🎮 Try it yourself",{"id":193,"html":194,"type":133},"b19","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-scroll-snap-native-carousel\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":196,"html":197,"type":133},"b20","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":199,"html":200,"type":133},"b21","\u003C!-- playground:end -->",{"id":202,"html":203,"text":203,"type":143,"level":31},"b22","The strictness question the demo above just answered",{"id":205,"html":206,"type":133},"b23","\u003Cp>\u003Ccode>scroll-snap-type\u003C\u002Fcode> takes two words: an axis (\u003Ccode>x\u003C\u002Fcode>, \u003Ccode>y\u003C\u002Fcode>, or \u003Ccode>both\u003C\u002Fcode>) and a strictness (\u003Ccode>mandatory\u003C\u002Fcode> or \u003Ccode>proximity\u003C\u002Fcode>).\u003C\u002Fp>",{"id":208,"type":209,"items":210,"ordered":18},"b24","list",[211,212],"\u003Cstrong>\u003Ccode>mandatory\u003C\u002Fcode>\u003C\u002Fstrong> — the browser will \u003Cem>always\u003C\u002Fem> rest on a snap point when the scroll ends. Flick it a tiny bit, it still jumps to the nearest card.","\u003Cstrong>\u003Ccode>proximity\u003C\u002Fcode>\u003C\u002Fstrong> — the browser only snaps if you land close enough to a snap point already. Scroll to the middle of nowhere and it just... stays there.",{"id":214,"html":215,"type":133},"b25","\u003Cp>Most carousels want \u003Ccode>mandatory\u003C\u002Fcode>. Long scrolling galleries where snapping should feel like a \u003Cem>nudge\u003C\u002Fem>, not a \u003Cem>rule\u003C\u002Fem>, want \u003Ccode>proximity\u003C\u002Fcode> — think a photo grid that snaps rows into place without ever fighting a scroll that&#39;s clearly headed somewhere else.\u003C\u002Fp>",{"id":217,"html":218,"text":219,"type":143,"level":31},"b26","The part that makes people say &quot;but what about—&quot;","The part that makes people say \"but what about—\"",{"id":221,"html":222,"type":133},"b27","\u003Cp>Two objections kill &quot;just use CSS&quot; conversations before they start, and both have answers.\u003C\u002Fp>",{"id":224,"html":225,"type":133},"b28","\u003Cp>\u003Cstrong>&quot;What if I want the next card to peek in at the edge?&quot;\u003C\u002Fstrong> That&#39;s \u003Ccode>scroll-padding\u003C\u002Fcode> on the container — it insets the snap area the same way \u003Ccode>padding\u003C\u002Fcode> insets content, so a card can snap to a position that isn&#39;t flush against the edge:\u003C\u002Fp>",{"id":227,"code":228,"type":150,"language":74,"highlight":229},"b29",".track {\n  scroll-snap-type: x mandatory;\n  scroll-padding-inline: 24px;\n}",[],{"id":231,"html":232,"type":133},"b30","\u003Cp>\u003Cstrong>&quot;What if a sticky header overlaps the top of my snapped section?&quot;\u003C\u002Fstrong> That&#39;s \u003Ccode>scroll-margin\u003C\u002Fcode> on the \u003Cem>target\u003C\u002Fem>, not the container — it pads the snap area around one specific element, so \u003Ccode>scroll-margin-top\u003C\u002Fcode> on a section keeps a sticky nav from covering its heading the moment it snaps into view. Same trick as \u003Ccode>scroll-padding\u003C\u002Fcode>, applied per-target instead of per-container.\u003C\u002Fp>",{"id":234,"html":235,"type":133},"b31","\u003Cp>Neither of those needs a resize listener recalculating offsets. They&#39;re declarative, and they respond to the viewport the same way the rest of your layout does.\u003C\u002Fp>",{"id":237,"html":238,"text":238,"type":143,"level":31},"b32","Where a hand-rolled scroller still wins",{"id":240,"html":241,"type":133},"b33","\u003Cp>I&#39;m not going to pretend CSS snap subsumes every carousel. Autoplay — advancing on a timer without user input — is still your \u003Ccode>setInterval\u003C\u002Fcode>, because CSS has no concept of &quot;and now do this without being touched.&quot; Infinite loop-around (card N wrapping back to card 1 seamlessly) is still awkward in pure CSS; you either fake it with cloned edge cards or accept a hard stop. And if you need to know the \u003Cem>exact\u003C\u002Fem> scroll-snap position programmatically — say, to sync a progress bar — you&#39;re back to a \u003Ccode>scroll\u003C\u002Fcode> listener, just a much simpler one than before, since you&#39;re reading position instead of fighting it.\u003C\u002Fp>",{"id":243,"html":244,"type":133},"b34","\u003Cp>The honest split: snapping, physics, and touch\u002Ftrackpad feel are the browser&#39;s job now. Autoplay and looping are still yours.\u003C\u002Fp>",{"id":246,"html":247,"text":247,"type":143,"level":31},"b35","The one-line version",{"id":249,"html":250,"type":133},"b36","\u003Cp>If your carousel&#39;s JavaScript exists to make scrolling \u003Cem>feel\u003C\u002Fem> like snapping into place, delete it and write \u003Ccode>scroll-snap-type\u003C\u002Fcode> and \u003Ccode>scroll-snap-align\u003C\u002Fcode> instead — the browser has owned that job, well, since 2019. Keep your JavaScript for the parts CSS genuinely can&#39;t do: autoplay, looping, and reading position back out.\u003C\u002Fp>",{"id":252,"html":253,"type":133},"b37","\u003Cp>What&#39;s still sitting in your codebase doing a job the platform quietly took over? Scroll snap was mine — what&#39;s yours?\u003C\u002Fp>",{"id":255,"html":256,"type":133},"b38","\u003C!-- quiz:start -->",{"id":258,"html":259,"text":259,"type":143,"level":31},"b39","🧠 Test yourself",{"id":261,"html":262,"type":133},"b40","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-scroll-snap-native-carousel\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":264,"html":265,"type":133},"b41","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":267,"html":268,"type":133},"b42","\u003C!-- quiz:end -->",{"id":270,"type":271},"b43","divider",{"id":273,"html":274,"type":133},"b44","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":276,"type":209,"items":277,"ordered":18},"b45",[278,279,280,281,282],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","The bug ticket said \"carousel feels broken on trackpad.\" It took me forty minutes to find the actual cause: my carousel library's `scrollTo` animation and the browser's own momentum scrolling were both trying to move the same element at the same time, and they disagreed.\n\nTwo `requestAnimationFrame` loops, fighting over one `scrollLeft` value, several times a second.\n\nI fixed it by deleting the library.\n\n## The carousel everyone hand-rolls\n\nHere's the pattern if you haven't built one recently: a flex row of cards, `overflow-x: auto`, and then JavaScript to make it feel like a carousel instead of a scrollbar with cards in it.\n\n```js\nfunction scrollToCard(index) {\n  const card = cards[index];\n  track.scrollTo({ left: card.offsetLeft, behavior: \"smooth\" });\n}\n\ntrack.addEventListener(\"scroll\", debounce(() => {\n  const nearest = findNearestCard(track.scrollLeft);\n  setActiveDot(nearest);\n}, 100));\n\nnextBtn.addEventListener(\"click\", () => scrollToCard(activeIndex + 1));\n```\n\nA `scroll` listener to track position. A debounce so it doesn't fire on every pixel. A `findNearestCard` function doing `offsetLeft` math. And the moment a user drags with a finger or a trackpad instead of clicking your arrows, you're racing the browser's native scroll physics with your own `scrollTo` calls — which is exactly the bug that ate my Tuesday.\n\nNone of that code is *wrong*, exactly. It's just solving a problem the browser already solved, badly enough that a plugin market exists around it.\n\n## What \"snap\" actually means to the browser\n\nCSS has had scroll snapping since 2019 — Baseline, in every current browser, no vendor prefix, no polyfill. It doesn't animate a scroll position for you. It changes where the browser lets a scroll gesture *rest*.\n\nTwo properties do the whole job. One goes on the scroll container:\n\n```css\n.track {\n  display: flex;\n  overflow-x: auto;\n  scroll-snap-type: x mandatory;\n}\n```\n\nOne goes on each card:\n\n```css\n.card {\n  scroll-snap-align: center;\n}\n```\n\nThat's it. Drag, flick, trackpad-swipe, or scrollbar-drag the track, and the browser's own scroll physics — the momentum, the deceleration curve, the bit that feels *right* on every input device because the browser's engineers tuned it for every input device — settles on whichever card's snap point is closest. No `scrollTo`, no debounce, no `findNearestCard`.\n\nTry dragging it yourself before you take the rest of my word for it.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-scroll-snap-native-carousel\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## The strictness question the demo above just answered\n\n`scroll-snap-type` takes two words: an axis (`x`, `y`, or `both`) and a strictness (`mandatory` or `proximity`).\n\n- **`mandatory`** — the browser will *always* rest on a snap point when the scroll ends. Flick it a tiny bit, it still jumps to the nearest card.\n- **`proximity`** — the browser only snaps if you land close enough to a snap point already. Scroll to the middle of nowhere and it just... stays there.\n\nMost carousels want `mandatory`. Long scrolling galleries where snapping should feel like a *nudge*, not a *rule*, want `proximity` — think a photo grid that snaps rows into place without ever fighting a scroll that's clearly headed somewhere else.\n\n## The part that makes people say \"but what about—\"\n\nTwo objections kill \"just use CSS\" conversations before they start, and both have answers.\n\n**\"What if I want the next card to peek in at the edge?\"** That's `scroll-padding` on the container — it insets the snap area the same way `padding` insets content, so a card can snap to a position that isn't flush against the edge:\n\n```css\n.track {\n  scroll-snap-type: x mandatory;\n  scroll-padding-inline: 24px;\n}\n```\n\n**\"What if a sticky header overlaps the top of my snapped section?\"** That's `scroll-margin` on the *target*, not the container — it pads the snap area around one specific element, so `scroll-margin-top` on a section keeps a sticky nav from covering its heading the moment it snaps into view. Same trick as `scroll-padding`, applied per-target instead of per-container.\n\nNeither of those needs a resize listener recalculating offsets. They're declarative, and they respond to the viewport the same way the rest of your layout does.\n\n## Where a hand-rolled scroller still wins\n\nI'm not going to pretend CSS snap subsumes every carousel. Autoplay — advancing on a timer without user input — is still your `setInterval`, because CSS has no concept of \"and now do this without being touched.\" Infinite loop-around (card N wrapping back to card 1 seamlessly) is still awkward in pure CSS; you either fake it with cloned edge cards or accept a hard stop. And if you need to know the *exact* scroll-snap position programmatically — say, to sync a progress bar — you're back to a `scroll` listener, just a much simpler one than before, since you're reading position instead of fighting it.\n\nThe honest split: snapping, physics, and touch\u002Ftrackpad feel are the browser's job now. Autoplay and looping are still yours.\n\n## The one-line version\n\nIf your carousel's JavaScript exists to make scrolling *feel* like snapping into place, delete it and write `scroll-snap-type` and `scroll-snap-align` instead — the browser has owned that job, well, since 2019. Keep your JavaScript for the parts CSS genuinely can't do: autoplay, looping, and reading position back out.\n\nWhat's still sitting in your codebase doing a job the platform quietly took over? Scroll snap was mine — what's yours?\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-scroll-snap-native-carousel\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":107,"canonical":285,"description":108},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-scroll-snap-native-carousel","019fff35-4908-727e-b7a3-dfff3df8ec8a",{"id":288,"locked":18},"019fff35-4930-752f-bb94-ae1242842119",[290],{"id":45,"slug":46,"title":48,"_count":291},{"questions":51},[293],{"locale":13,"slug":46},{"id":45,"slug":46,"title":48,"_count":295,"questionCount":51},{"questions":51}]