From 425da73930da9030f7ab8c17bd53f67882754f75 Mon Sep 17 00:00:00 2001
From: Colin Kallemein <cakallemein@gmail.com>
Date: Sat, 6 Jul 2024 20:38:33 +0200
Subject: [PATCH] Convert character screen to Tailwind

---
 src/screens/Characters.vue | 262 ++++++-------------------------------
 tailwind.config.js         |   7 +
 2 files changed, 48 insertions(+), 221 deletions(-)

diff --git a/src/screens/Characters.vue b/src/screens/Characters.vue
index a22e8db..e4b42ab 100644
--- a/src/screens/Characters.vue
+++ b/src/screens/Characters.vue
@@ -1,38 +1,40 @@
 <template>
-  <div class="character-select-screen">
-    <div class="ui-wrapper">
-      <div class="characters-wrapper" v-if="!isLoading">
-        <div v-for="character in characters" :key="character.id" class="character" :class="{ active: selected_character == character.id }">
-          <input type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" />
-          <label :for="character.id">{{ character.name }}</label>
+  <div class="character-select-screen bg-gray-300 relative">
+    <div class="absolute bg-[url('/assets/shapes/select-screen-bg-shape.svg')] bg-no-repeat bg-center w-full h-full"></div>
+    <div class="ui-wrapper h-dvh flex flex-col justify-center items-center gap-[80px] px-[80px]">
+      <div class="filler"></div>
+      <div class="characters-wrapper flex justify-center flex-wrap gap-[60px] w-full max-h-[650px] overflow-auto" v-if="!isLoading">
+        <div v-for="character in characters" :key="character.id" class="character m-[15px] w-[170px] h-[275px] flex flex-col rounded-[20px] relative bg-[url('/assets/shapes/character-select-shape.svg')] bg-no-repeat shadow-character" :class="{ active: selected_character == character.id }">
+          <input class="opacity-0 h-full w-full absolute m-0 z-10" type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" />
+          <label class="font-bold absolute left-1/2 top-[20px] max-w-[130px] translate-x-[-50%] translate-y-[-50%] text-center text-ellipsis overflow-hidden whitespace-nowrap drop-shadow-text" :for="character.id">{{ character.name }}</label>
           <!-- @TODO : Add a confirmation dialog -->
-          <button class="delete" @click="delete_character(character.id)">
+          <button class="delete bg-red w-[30px] h-[30px] p-[3px] rounded-full absolute right-[-15px] top-0 translate-y-[-50%] z-10 border-2 border-solid border-white hover:bg-red-100" @click="delete_character(character.id)">
             <img draggable="false" src="/assets/icons/trashcan.svg" />
           </button>
 
-          <div class="sprite-container">
-            <img draggable="false" src="/assets/avatar/default/0.png" />
+          <div class="sprite-container flex flex-col items-center m-auto">
+            <img class="drop-shadow-20" draggable="false" src="/assets/avatar/default/0.png" />
           </div>
-          <span>Lvl. {{ character.level }}</span>
+          <span class="absolute bottom-[20px] w-full text-center translate-y-1/2 z-10 drop-shadow-text">Lvl. {{ character.level }}</span>
+          <div class="selected-character absolute max-w-0 w-[65%] h-[3px] bg-white rounded-[3px] left-1/2 bottom-[-15px] translate-x-[-50%] transition-all ease-in-out duration-300"></div>
         </div>
 
-        <div class="character new-character" v-if="characters.length < 4">
-          <button @click="isModalOpen = true">
-            <img draggable="false" src="/assets/icons/plus-icon.svg" />
-            <span>Create new</span>
+        <div class="character new-character m-[15px] w-[170px] h-[275px] flex flex-col rounded-[20px] relative bg-gray-50 bg-opacity-50 bg-no-repeat shadow-character" v-if="characters.length < 4">
+          <button class="h-full w-full py-[40px] flex flex-col justify-between" @click="isModalOpen = true">
+            <div class="filler"></div>
+            <img class="w-[100px] h-[100px] m-auto" draggable="false" src="/assets/icons/plus-icon.svg" />
+            <span class="self-center text-base absolute bottom-[20px] w-full text-center translate-y-1/2 z-10 drop-shadow-text">Create new</span>
           </button>
         </div>
       </div>
       <div v-else>
-        <div class="loading-spinner">
-          <img src="/assets/icons/loading-icon1.svg" />
-        </div>
+        <img class="w-[80px] invert-80" src="/assets/icons/loading-icon1.svg" />
       </div>
 
-      <div class="button-wrapper" v-if="!isLoading">
-        <button class="btn-cyan" :disabled="!selected_character" @click="select_character()">
+      <div class="button-wrapper flex gap-[30px]" v-if="!isLoading">
+        <button class="btn-cyan py-2 pr-2.5 pl-[30px] min-w-[100px] relative rounded text-xl flex gap-[15px] items-center transition-all ease-in-out duration-200" :disabled="!selected_character" @click="select_character()">
           PLAY
-          <img draggable="false" src="/assets/icons/arrow.svg" />
+          <img class="h-[30px] drop-shadow-20" draggable="false" src="/assets/icons/arrow.svg" />
         </button>
       </div>
     </div>
@@ -114,209 +116,27 @@ onBeforeUnmount(() => {
 </script>
 
 <style lang="scss">
-@import '@/assets/scss/main';
-
-.character-select-screen {
-  &::before {
-    content: '';
-    position: absolute;
-    background-image: url('/assets/shapes/select-screen-bg-shape.svg');
-    background-repeat: no-repeat;
-    background-position: center;
-    background-size: 100% cover;
-    width: 100%;
-    height: 100%;
-  }
-  background-color: $dark-gray;
-  position: relative;
-
-  .ui-wrapper {
-    height: 100vh;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    align-items: center;
-    gap: 80px;
-    padding: 0 80px;
-    &::before {
-      content: '';
-    }
-
-    .loading-spinner img {
-      width: 5rem;
-      // css color
-      filter: invert(1);
-      // white
-      filter: invert(80%);
-    }
-
-    .characters-wrapper {
-      display: flex;
-      justify-content: center;
-      flex-wrap: wrap;
-      gap: 60px;
-      width: 100%;
-      max-height: 650px;
-      overflow: auto;
-
-      .character {
-        margin: 15px;
-        width: 170px;
-        height: 275px;
-        display: flex;
-        flex-direction: column;
-        border-radius: 20px;
-        position: relative;
-        background-repeat: no-repeat;
-        background-image: url('/assets/shapes/character-select-shape.svg');
-        box-shadow: 0 4px 30px rgba($black, 0.1);
-
-        &::after {
-          content: '';
-          position: absolute;
-          max-width: 0;
-          width: 65%;
-          height: 3px;
-          background-color: $white;
-          border-radius: 3px;
-          left: 50%;
-          bottom: -15px;
-          transform: translateX(-50%);
-          transition: ease-in-out max-width 0.3s;
-        }
-
-        &.active::after {
-          max-width: 170px;
-        }
-
-        &.new-character {
-          background-color: rgba($light-gray, 0.5);
-          background-image: none;
-
-          button {
-            height: 100%;
-            width: 100%;
-            padding: 40px 0;
-            display: flex;
-            flex-direction: column;
-            justify-content: space-between;
-            &::before {
-              content: '';
-            }
-            img {
-              width: 100px;
-              height: 100px;
-              margin: auto;
-            }
-
-            span {
-              align-self: center;
-              font-size: 1rem;
-            }
-          }
-        }
-
-        input[type='radio'] {
-          opacity: 0;
-          height: 100%;
-          width: 100%;
-          position: absolute;
-          margin: 0;
-          z-index: 9;
-        }
-
-        label {
-          font-weight: bold;
-          position: absolute;
-          left: 50%;
-          top: 20px;
-          max-width: 130px;
-          transform: translateY(-50%) translateX(-50%);
-          text-align: center;
-          text-overflow: ellipsis;
-          overflow: hidden;
-          white-space: nowrap;
-          text-shadow: 1px 1px 5px rgba($black, 0.25);
-        }
-
-        button.delete {
-          background-color: $red;
-          width: 30px;
-          height: 30px;
-          padding: 3px;
-          border-radius: 100%;
-          position: absolute;
-          right: -15px;
-          top: 0;
-          transform: translateY(-50%);
-          z-index: 1;
-          border: 2px solid $white;
-
-          &:hover {
-            background-color: $dark-red;
-          }
-        }
-
-        span {
-          position: absolute;
-          bottom: 20px;
-          width: 100%;
-          text-align: center;
-          transform: translateY(50%);
-          z-index: 1;
-          text-shadow: 1px 1px 5px rgba($black, 0.25);
-        }
-
-        .sprite-container {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          margin: auto;
-
-          img {
-            filter: drop-shadow(0 3px 6px rgba($black, 0.25));
-          }
-        }
-      }
-    }
-
-    .button-wrapper {
-      display: flex;
-      gap: 30px;
-
-      button {
-        padding: 8px 10px 8px 30px;
-        min-width: 100px;
-        position: relative;
-        border-radius: 5px;
-        font-size: 1.25rem;
-        display: flex;
-        gap: 15px;
-        align-items: center;
-        transition: ease-in-out gap 0.2s;
-
-        span {
-          margin: auto;
-        }
-
-        img {
-          height: 30px;
-          filter: drop-shadow(0 4px 6px rgba($black, 0.25));
-        }
-
-        &:disabled {
-          background-color: rgba($cyan, 0.5);
-          cursor: not-allowed;
-          &:hover {
-            gap: 15px;
-          }
-        }
-
-        &:hover {
-          gap: 20px;
-        }
+.characters-wrapper {
+  .character {
+    &.active {
+      .selected-character {
+        @apply max-w-[170px];
       }
     }
   }
 }
+
+.button-wrapper {
+  button {
+    &:disabled {
+      @apply bg-cyan bg-opacity-50 cursor-not-allowed;
+      &:hover {
+        @apply gap-[15px];
+      }
+    }
+    &:hover {
+      @apply gap-[20px];
+    }
+  }
+}
 </style>
diff --git a/tailwind.config.js b/tailwind.config.js
index 2053f71..334344e 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -11,10 +11,17 @@ export default {
       '30px': '30px'
     },
     extend: {
+      invert: {
+        80: '0.80'
+      },
       dropShadow: {
         'default': '0 3px 6px rgb(0, 0, 0)',
+        'text': '1px 1px 5px rgba(0, 0, 0, 0.25)',
         '20': '0 3px 6px rgba(0, 0, 0, 0.2)'
       },
+      boxShadow: {
+        'character': '0 4px 30px rgba(0, 0, 0, 0.1)',
+      },
       colors: {
         red: {
           DEFAULT: '#d50000',