From 577d1ed778fb0989823624edf85f1c4ae1902c5a Mon Sep 17 00:00:00 2001
From: Jonathan Reinink <jonathan@reinink.ca>
Date: Wed, 29 Sep 2021 06:21:43 -0400
Subject: [PATCH] Add `scroll-snap` utilities

Co-Authored-By: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
---
 src/corePlugins.js          | 70 +++++++++++++++++++++++++++++++++++++
 stubs/defaultConfig.stub.js |  5 +++
 tests/basic-usage.test.css  | 19 ++++++++++
 tests/basic-usage.test.html |  4 +++
 4 files changed, 98 insertions(+)

diff --git a/src/corePlugins.js b/src/corePlugins.js
index 18bbeddb64c9..c74982bcb7f9 100644
--- a/src/corePlugins.js
+++ b/src/corePlugins.js
@@ -813,6 +813,76 @@ export default {
     })
   },
 
+  scrollSnapType: ({ addUtilities, addBase }) => {
+    addBase({
+      '@defaults scroll-snap-type': {
+        '--tw-scroll-snap-strictness': 'proximity',
+      },
+    })
+
+    addUtilities({
+      '.snap-none': { 'scroll-snap-type': 'none' },
+      '.snap-x': {
+        '@defaults scroll-snap-type': {},
+        'scroll-snap-type': 'x var(--tw-scroll-snap-strictness)',
+      },
+      '.snap-y': {
+        '@defaults scroll-snap-type': {},
+        'scroll-snap-type': 'y var(--tw-scroll-snap-strictness)',
+      },
+      '.snap-both': {
+        '@defaults scroll-snap-type': {},
+        'scroll-snap-type': 'both var(--tw-scroll-snap-strictness)',
+      },
+      '.snap-mandatory': { '--tw-scroll-snap-strictness': 'mandatory' },
+      '.snap-proximity': { '--tw-scroll-snap-strictness': 'proximity' },
+    })
+  },
+
+  scrollSnapAlign: ({ addUtilities }) => {
+    addUtilities({
+      '.snap-start': { 'scroll-snap-align': 'start' },
+      '.snap-end': { 'scroll-snap-align': 'end' },
+      '.snap-center': { 'scroll-snap-align': 'center' },
+      '.snap-align-none': { 'scroll-snap-align': 'none' },
+    })
+  },
+
+  scrollSnapStop: ({ addUtilities }) => {
+    addUtilities({
+      '.snap-normal': { 'scroll-snap-stop': 'normal' },
+      '.snap-always': { 'scroll-snap-stop': 'always' },
+    })
+  },
+
+  scrollMargin: createUtilityPlugin('scrollMargin', [
+    ['scroll-m', ['scroll-margin']],
+    [
+      ['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
+      ['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
+    ],
+    [
+      ['scroll-mt', ['scroll-margin-top']],
+      ['scroll-mr', ['scroll-margin-right']],
+      ['scroll-mb', ['scroll-margin-bottom']],
+      ['scroll-ml', ['scroll-margin-left']],
+    ],
+  ]),
+
+  scrollPadding: createUtilityPlugin('scrollPadding', [
+    ['scroll-p', ['scroll-padding']],
+    [
+      ['scroll-px', ['scroll-padding-left', 'scroll-padding-right']],
+      ['scroll-py', ['scroll-padding-top', 'scroll-padding-bottom']],
+    ],
+    [
+      ['scroll-pt', ['scroll-padding-top']],
+      ['scroll-pr', ['scroll-padding-right']],
+      ['scroll-pb', ['scroll-padding-bottom']],
+      ['scroll-pl', ['scroll-padding-left']],
+    ],
+  ]),
+
   listStylePosition: ({ addUtilities }) => {
     addUtilities({
       '.list-inside': { 'list-style-position': 'inside' },
diff --git a/stubs/defaultConfig.stub.js b/stubs/defaultConfig.stub.js
index 80abe1896ec9..cae37176c43d 100644
--- a/stubs/defaultConfig.stub.js
+++ b/stubs/defaultConfig.stub.js
@@ -711,6 +711,11 @@ module.exports = {
       125: '1.25',
       150: '1.5',
     },
+    scrollMargin: (theme, { negative }) => ({
+      ...theme('spacing'),
+      ...negative(theme('spacing')),
+    }),
+    scrollPadding: (theme) => theme('spacing'),
     sepia: {
       0: '0',
       DEFAULT: '100%',
diff --git a/tests/basic-usage.test.css b/tests/basic-usage.test.css
index eed0e41014ce..020ee1ef5aec 100644
--- a/tests/basic-usage.test.css
+++ b/tests/basic-usage.test.css
@@ -11,6 +11,7 @@
   --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y))
     rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
     scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
+  --tw-scroll-snap-strictness: proximity;
   --tw-border-opacity: 1;
   border-color: rgb(229 231 235 / var(--tw-border-opacity));
   --tw-ring-offset-shadow: 0 0 #0000;
@@ -311,6 +312,24 @@
 .resize-none {
   resize: none;
 }
+.snap-x {
+  scroll-snap-type: x var(--tw-scroll-snap-strictness);
+}
+.snap-mandatory {
+  --tw-scroll-snap-strictness: mandatory;
+}
+.snap-center {
+  scroll-snap-align: center;
+}
+.snap-always {
+  scroll-snap-stop: always;
+}
+.scroll-mt-6 {
+  scroll-margin-top: 1.5rem;
+}
+.scroll-p-6 {
+  scroll-padding: 1.5rem;
+}
 .list-inside {
   list-style-position: inside;
 }
diff --git a/tests/basic-usage.test.html b/tests/basic-usage.test.html
index 91e4bced1f27..026c47d85f1a 100644
--- a/tests/basic-usage.test.html
+++ b/tests/basic-usage.test.html
@@ -110,6 +110,10 @@
     <div class="pointer-events-none"></div>
     <div class="absolute"></div>
     <div class="resize-none"></div>
+    <div class="snap-x snap-mandatory"></div>
+    <div class="snap-center snap-always"></div>
+    <div class="scroll-mt-6"></div>
+    <div class="scroll-p-6"></div>
     <div class="ring-white"></div>
     <div class="ring-offset-blue-300"></div>
     <div class="ring-offset-2"></div>