Introducing: Swipe Cards Collection (AI Component + JSON)

MaximeBaker
Tue, 18 Mar 2025 02:30:56 +0000
Community Resources

A new way to show data

Hello Fellow Gliders

I’m excited to share a versatile Swipe Cards Collection I created using an AI Component and JSON. Though it might remind you of Tinder, its applications are extensive. This post will show you what it does and guide you through the implementation.

First, here is the baby

Fine-tuning

You can change anything you want in it by asking the AI to make so changes. Remember that the AI in the Glide AI Component doesn’t remember previous message. It only know the current message context and the code already in place. So make sure to do good prompts.

JSON Configuration

Create the needed columns in your desired table. By using my default AI Component, here is the columns configuration and the JSON Object.

Then, you need to create a joined list and a template to make a JSON Array of all the rows. You can use query column before joined list if you need to filter the data.

Likes Counter

You can now create, if needed, the likes counter.
Create a likes-dislikes table.

Then, create the relation.

Context

Add a user-specific text column that will store current Card JSON object in it to be able to execute action in the right row context from the AI Component.

AI Component

You first need to create the variables and actions:

For the user id, you can use row id of the users table. For the example, I forgot to create the row id, so I used email instead.

Then paste:

Do exactly this :
:root {--color-accent: 215deg 99% 28%;

}




      html, body {
        font-family: Inter,-apple-system,BlinkMacSystemFont,Roboto,sans-serif;
        background-color: transparent;
      }


      import { init } from "https://go.glideapps.com/shell/v1.js";
      init({"fieldsDataObject":{"currentCard":"{\"Name\":\"Item Title\",\"Description\":\"A short description of the item\",\"Category\":\"Category 1\",\"Image\":\"https://images.pexels.com/photos/158827/field-corn-air-frisch-158827.jpeg\",\"ID\":\"a-k3jPSWlSAKokMunvj5TDg\",\"Likes\":18}"},"schema":[{"id":"jsonArray","type":"string","name":"jsonArray"},{"id":"currentCard","type":"string","name":"currentCard"}],"actions":[{"title":"swipedLeft","id":"swipedLeft"},{"title":"swipedRight","id":"swipedRight"}],"config":{"colorScheme":"light","cssVariables":{"--color-accent":"215deg 99% 28%"},"alpineConfig":{"mode":"standalone","hasBodyAction":false}},"debug":false})


/*! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com*/*,:after,:before{border-color:hsl(var(--color-gray-200)/1);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:hsl(var(--color-gray-400)/1);opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.headline-lg,.headline-md,.headline-sm,.headline-xl,.headline-xs{font-weight:600}.headline-sm{font-size:20px;letter-spacing:-.025em;line-height:1.375}@media (min-width:768px){.headline-sm{font-size:1.25rem;line-height:1.75rem}}.body-base{font-size:1rem;line-height:1.5rem;line-height:1.5}@media (min-width:768px){.body-base{font-size:.875rem;line-height:1.25rem}}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-x-0{left:0;right:0}.bottom-0{bottom:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-2{margin-bottom:.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.line-clamp-2{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.h-14{height:3.5rem}.h-\[600px\]{height:600px}.h-full{height:100%}.w-14{width:3.5rem}.w-full{width:100%}.max-w-sm{max-width:24rem}.transform{transform:translate(var(--tw-translate-x),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))}.cursor-pointer{cursor:pointer}.select-none{user-select:none}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.overflow-hidden{overflow:hidden}.rounded-full{border-radius:9999px}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:hsl(var(--color-gray-200)/var(--tw-border-opacity))}.bg-accent{--tw-bg-opacity:1;background-color:hsl(var(--color-accent)/var(--tw-bg-opacity))}.bg-background{--tw-bg-opacity:1;background-color:hsl(var(--color-background-DEFAULT)/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-opacity-10{--tw-bg-opacity:0.1}.bg-opacity-20{--tw-bg-opacity:0.2}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-black{--tw-gradient-from:#000 var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.object-cover{object-fit:cover}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.text-accent{--tw-text-opacity:1;color:hsl(var(--color-accent)/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-opacity-80{--tw-text-opacity:0.8}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\@container{container-type:inline-size}@layer colors{:root.color-scheme-auto{color-scheme:light;--color-gray-0:0deg 0% 100%;--color-gray-50:0deg 0% 98%;--color-gray-100:0deg 0% 96%;--color-gray-200:0deg 0% 94%;--color-gray-300:0deg 0% 88%;--color-gray-400:0deg 0% 68%;--color-gray-500:0deg 0% 56%;--color-gray-600:0deg 0% 44%;--color-gray-700:0deg 0% 31%;--color-gray-800:0deg 0% 20%;--color-gray-900:0deg 0% 5%;--color-gray-950:0deg 0% 0%;--color-background-DEFAULT:0deg 0% 100%}@media (prefers-color-scheme:dark){:root.color-scheme-auto{color-scheme:dark;--color-gray-0:0deg 0% 4%;--color-gray-50:0deg 0% 9%;--color-gray-100:0deg 0% 12%;--color-gray-200:0deg 0% 15%;--color-gray-300:0deg 0% 20%;--color-gray-400:0deg 0% 31%;--color-gray-500:0deg 0% 44%;--color-gray-600:0deg 0% 57%;--color-gray-700:0deg 0% 72%;--color-gray-800:0deg 0% 90%;--color-gray-900:0deg 0% 100%;--color-gray-950:0deg 0% 100%;--color-background-DEFAULT:0deg 0% 9%}}:root.color-scheme-light{color-scheme:light;--color-gray-0:0deg 0% 100%;--color-gray-50:0deg 0% 98%;--color-gray-100:0deg 0% 96%;--color-gray-200:0deg 0% 94%;--color-gray-300:0deg 0% 88%;--color-gray-400:0deg 0% 68%;--color-gray-500:0deg 0% 56%;--color-gray-600:0deg 0% 44%;--color-gray-700:0deg 0% 31%;--color-gray-800:0deg 0% 20%;--color-gray-900:0deg 0% 5%;--color-gray-950:0deg 0% 0%;--color-background-DEFAULT:0deg 0% 100%}:root.color-scheme-dark{color-scheme:dark;--color-gray-0:0deg 0% 4%;--color-gray-50:0deg 0% 9%;--color-gray-100:0deg 0% 12%;--color-gray-200:0deg 0% 15%;--color-gray-300:0deg 0% 20%;--color-gray-400:0deg 0% 31%;--color-gray-500:0deg 0% 44%;--color-gray-600:0deg 0% 57%;--color-gray-700:0deg 0% 72%;--color-gray-800:0deg 0% 90%;--color-gray-900:0deg 0% 100%;--color-gray-950:0deg 0% 100%;--color-background-DEFAULT:0deg 0% 9%}}.hover\:bg-red-50:hover{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}@media (min-width:640px){.sm\:px-5{padding-left:1.25rem;padding-right:1.25rem}}@media (min-width:768px){.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:1024px){.lg\:px-7{padding-left:1.75rem;padding-right:1.75rem}}@media (min-width:1280px){.xl\:px-8{padding-left:2rem;padding-right:2rem}}






              <div class="absolute inset-0 bg-background rounded-xl border border-gray-200 transition-all duration-300 ease-out overflow-hidden" :style="{
                      opacity: index === currentIndex ? 1 : 0,
                      zIndex: JSON.parse(jsonArray).length - Math.abs(index - currentIndex),
                      transform: getCardTransform(index),
                      transition: 'transform 0.3s ease-out, opacity 0.3s ease-out'
                   }" @mousedown="startDrag($event, index)" @touchstart="startDrag($event, index)" @mousemove="drag($event, index)" @touchmove="drag($event, index)" @mouseup="endDrag($event, index)" @touchend="endDrag($event, index)" @mouseleave="endDrag($event, index)">









                              ✕


❤









          document.addEventListener('alpine:init', () => {
              Alpine.data('swipeCards', () => ({
                  startX: 0,
                  currentX: 0,
                  isDragging: false,
                  threshold: 100,
                  currentIndex: 0,
                  cardOffsets: {},

                  init() {
                      this.$watch('jsonArray', () => this.updateCurrentCard());
                      this.$watch('currentIndex', () => this.updateCurrentCard());
                      this.$nextTick(() => setTimeout(() => this.updateCurrentCard(), 250));
                  },

                  updateCurrentCard() {
                      const cards = JSON.parse(this.jsonArray || '[]');
                      if (cards.length > 0 && this.currentIndex < cards.length) {
                          this.currentCard = JSON.stringify(cards[this.currentIndex]);
                      }
                  },

                  getCardTransform(index) {
                      const offset = this.cardOffsets[index] || 0;
                      const scale = index === this.currentIndex ? 1 : 0.8;
                      return `translateX(${offset}px) rotate(${offset * 0.1}deg) scale(${scale})`;
                  },

                  startDrag(e, index) {
                      if (e.target.closest('button')) return;
                      this.isDragging = true;
                      this.startX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX;
                      this.currentX = this.startX;
                      this.draggedIndex = index;
                  },

                  drag(e, index) {
                      if (!this.isDragging || this.draggedIndex !== index) return;
                      this.currentX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX;
                      const deltaX = this.currentX - this.startX;
                      this.cardOffsets[index] = deltaX;
                  },

                  endDrag(e, index) {
                      if (!this.isDragging || this.draggedIndex !== index) return;
                      this.isDragging = false;
                      const deltaX = this.currentX - this.startX;
                      const direction = deltaX > 0 ? 'right' : 'left';
                      if (Math.abs(deltaX) > this.threshold) {
                          this.swipe(direction, index);
                      } else {
                          this.cardOffsets[index] = 0;
                      }
                      this.draggedIndex = null;
                  },

                  swipe(direction, index) {
                      const swipeAnimation = direction === 'left' ? -150 : 150;
                      this.cardOffsets[index] = swipeAnimation;

                      setTimeout(() => {
                          this.currentIndex = (this.currentIndex + 1) % JSON.parse(this.jsonArray).length;
                          this.updateCurrentCard();
                          this.cardOffsets[index] = 0;
                          if (direction === 'left') {
                              this.swipedLeft();
                          } else {
                              this.swipedRight();
                          }
                      }, 500);
                  },

                  swipedLeft() {
                      // Call the provided action
                      if (typeof swipedLeft === 'function') {
                          swipedLeft();
                      }
                  },

                  swipedRight() {
                      // Call the provided action
                      if (typeof swipedRight === 'function') {
                          swipedRight();
                      }
                  }
              }));
          });






Happy Gliding!

Hope you like it! Feel free to comment or ask any questions.

33 posts - 8 participants