45 | ├── public ├── card.png ├── cc-jcb.png ├── arrowUp.png ├── cc-amex.png ├── cc-diners.png ├── cc-discover.png ├── cc-invalid.png ├── cc-maestro.png ├── cc-unionpay.png ├── github-logo.png ├── cc-mastercard.png ├── discord-logo.png ├── linkedin-logo.png ├── telegram-logo.png ├── whatsapp-logo.png ├── instagram-logo.png ├── cc-default.svg ├── cc-chip.svg ├── cc-icon.svg └── cc-visa.svg ├── .github └── project.png ├── .gitignore ├── package.json ├── LICENSE ├── src ├── css │ ├── index.scss │ ├── _credit-card.scss │ ├── _swal.scss │ ├── index.css.map │ ├── _form.scss │ ├── _base.scss │ └── index.css └── main.js ├── readme-en.md ├── README.md └── index.html /public/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/card.png -------------------------------------------------------------------------------- /public/cc-jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-jcb.png -------------------------------------------------------------------------------- /.github/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/.github/project.png -------------------------------------------------------------------------------- /public/arrowUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/arrowUp.png -------------------------------------------------------------------------------- /public/cc-amex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-amex.png -------------------------------------------------------------------------------- /public/cc-diners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-diners.png -------------------------------------------------------------------------------- /public/cc-discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-discover.png -------------------------------------------------------------------------------- /public/cc-invalid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-invalid.png -------------------------------------------------------------------------------- /public/cc-maestro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-maestro.png -------------------------------------------------------------------------------- /public/cc-unionpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-unionpay.png -------------------------------------------------------------------------------- /public/github-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/github-logo.png -------------------------------------------------------------------------------- /public/cc-mastercard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/cc-mastercard.png -------------------------------------------------------------------------------- /public/discord-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/discord-logo.png -------------------------------------------------------------------------------- /public/linkedin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/linkedin-logo.png -------------------------------------------------------------------------------- /public/telegram-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/telegram-logo.png -------------------------------------------------------------------------------- /public/whatsapp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/whatsapp-logo.png -------------------------------------------------------------------------------- /public/instagram-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luk4x/gencard/HEAD/public/instagram-logo.png -------------------------------------------------------------------------------- /public/cc-default.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | !.vscode/settings.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GenCard", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^3.1.0" 13 | }, 14 | "dependencies": { 15 | "dom-to-image": "^2.6.0", 16 | "imask": "^6.4.3", 17 | "sweetalert": "^2.1.2", 18 | "vanilla-tilt": "^1.7.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/cc-chip.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lucas Maciel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/css/index.scss: -------------------------------------------------------------------------------- 1 | @import 'base'; 2 | @import 'credit-card'; 3 | @import 'form'; 4 | @import 'swal'; 5 | 6 | @media (max-width: 440px) { 7 | :root { 8 | font-size: 50%; 9 | } 10 | 11 | body { 12 | background: linear-gradient(5deg, $neutral-color2 50.5%, $neutral-color1 49.5%); 13 | 14 | #app { 15 | background: none; 16 | border: none; 17 | 18 | .cc-bg svg { 19 | width: 100%; 20 | height: auto; 21 | } 22 | } 23 | } 24 | } 25 | 26 | @media (min-width: 840px) { 27 | body { 28 | padding: 30px; 29 | background: linear-gradient(45deg, $neutral-color1 61.5%, $neutral-color2 39.5%); 30 | 31 | #app { 32 | grid-template-areas: 33 | 'A B' 34 | 'C B'; 35 | width: fit-content; 36 | max-width: fit-content; 37 | padding-inline: 4.8rem; 38 | grid-template-columns: 0.8fr 1fr; 39 | background: linear-gradient(-45deg, $neutral-color1 61.5%, $neutral-color2 39.5%); 40 | 41 | header { 42 | grid-area: A; 43 | } 44 | 45 | section.cc { 46 | grid-area: B; 47 | align-self: center; 48 | } 49 | 50 | form { 51 | grid-area: C; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/css/_credit-card.scss: -------------------------------------------------------------------------------- 1 | .cc { 2 | width: 36.5rem; 3 | height: 24rem; 4 | padding: 2rem; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | z-index: 1; 9 | background: $neutral-color1; 10 | border-radius: 20px; 11 | border: 1px solid #ffffff20; 12 | 13 | .cc-bg { 14 | position: absolute; 15 | inset: 0; 16 | z-index: 1; 17 | } 18 | 19 | .cc-logo { 20 | display: flex; 21 | justify-content: space-between; 22 | align-items: flex-start; 23 | 24 | span:nth-child(2) img { 25 | transition: all 0.8s ease-in-out; 26 | opacity: 0; 27 | max-height: 35px; 28 | transform: translateY(-5px); 29 | } 30 | } 31 | 32 | .cc-info { 33 | display: grid; 34 | gap: 1.4rem; 35 | 36 | .cc-number { 37 | font-size: 2.4rem; 38 | font-weight: bold; 39 | line-height: 2.9rem; 40 | letter-spacing: 0.04em; 41 | color: $text-color; 42 | text-shadow: 0 0.12rem 0.32rem rgb(23 23 23 / 40%); 43 | } 44 | 45 | .cc-holder, 46 | .cc-expiration, 47 | .cc-security { 48 | display: grid; 49 | gap: 0.4rem; 50 | } 51 | 52 | .cc-extra { 53 | display: flex; 54 | justify-content: space-between; 55 | align-items: center; 56 | } 57 | } 58 | 59 | .label { 60 | font-size: 1rem; 61 | line-height: 1.2rem; 62 | } 63 | 64 | .value { 65 | font-size: 1.4rem; 66 | font-weight: bold; 67 | color: $text-color; 68 | text-shadow: 0 0.25rem 0.25rem rgb(22 22 22 / 16%); 69 | text-transform: uppercase; 70 | } 71 | } 72 | 73 | .cc, 74 | .cc-logo, 75 | .cc-number { 76 | position: relative; 77 | z-index: 1; 78 | } 79 | -------------------------------------------------------------------------------- /public/cc-icon.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /public/cc-visa.svg: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/css/_swal.scss: -------------------------------------------------------------------------------- 1 | .swal-overlay { 2 | background-color: #8257e670; 3 | 4 | .swal-modal { 5 | background-color: $neutral-color1; 6 | border: 2px solid $neutral-color2; 7 | 8 | .swal-icon::before, 9 | .swal-icon::after, 10 | .swal-icon--success__hide-corners { 11 | background: $neutral-color1; 12 | } 13 | 14 | .swal-icon--success { 15 | border-color: $secondary-color; 16 | } 17 | 18 | .swal-icon--success__line { 19 | background-color: $secondary-color; 20 | } 21 | 22 | .swal-text { 23 | color: $text-color; 24 | font-size: 17px; 25 | text-align: center; 26 | margin-top: 30px; 27 | display: block; 28 | } 29 | 30 | .swal-title { 31 | text-align: center; 32 | text-transform: capitalize; 33 | font-size: 33px; 34 | color: $dark-primary-color; 35 | border-bottom: 2px solid $secondary-color; 36 | border-radius: 2px; 37 | display: inline; 38 | } 39 | 40 | .swal-content__img { 41 | width: 100%; 42 | max-width: 288px; 43 | } 44 | 45 | .swal-button-container { 46 | border-radius: 6px; 47 | overflow: hidden; 48 | 49 | .swal-button.swal-button--confirm { 50 | display: flex; 51 | justify-content: center; 52 | align-items: center; 53 | flex-direction: column; 54 | color: $text-color; 55 | background: $primary-color; 56 | width: 110px; 57 | height: 45px; 58 | border: 2px solid $secondary-color; 59 | transition: 0.2s all ease-in-out; 60 | 61 | &:hover { 62 | background: $dark-primary-color; 63 | 64 | &::before { 65 | top: 10px; 66 | } 67 | 68 | &::after { 69 | bottom: -40px; 70 | } 71 | } 72 | 73 | &::before { 74 | content: url('https://user-images.githubusercontent.com/86276393/203387682-dee65164-c138-49c4-b39c-8547aafa0140.png'); 75 | position: absolute; 76 | top: -40px; 77 | transition: all 0.4s ease-in-out; 78 | } 79 | 80 | &::after { 81 | content: 'Download'; 82 | position: absolute; 83 | bottom: 14px; 84 | transition: all 0.4s ease-in-out; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/css/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["_base.scss","_credit-card.scss","_form.scss","_swal.scss","index.scss"],"names":[],"mappings":"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","file":"index.css"} -------------------------------------------------------------------------------- /readme-en.md: -------------------------------------------------------------------------------- 1 |
| 4 | 🇺🇸 English 5 | | 6 |
| 9 | 🇧🇷 Português 10 | | 11 |
20 | Video | 21 | Technologies | 22 | About | 23 | Highlights | 24 | Cloning | 25 | Contact 26 |
27 ||
96 |
97 |
123 |
98 | Vitrine.Dev 🪟
99 |
115 |
116 | | :placard: Vitrine.Dev | Lucas Maciel |
117 | | ------------- | --- |
118 | | :sparkles: Nome | **💳 GenCard**
119 | | :label: Tecnologias | sass, regex, imask, sweetalert, nodejs, npm, css-buttons, dom-to-image, vitejs, vanilla-tilt, ui-ball-loaders, javascript, css, html
120 | | :camera: Img |
102 |
103 | |
112 | 106 | 107 | 108 | Lucas Maciel 109 | 110 | 111 | |
124 |
125 | MaykBrito.Dev 🪟
126 |
|
142 |
146 | Back to Top 147 |
148 | -------------------------------------------------------------------------------- /src/css/_form.scss: -------------------------------------------------------------------------------- 1 | form { 2 | max-width: 36rem; 3 | display: grid; 4 | gap: 1.6rem; 5 | 6 | .input-wrapper { 7 | display: grid; 8 | gap: 0.6rem; 9 | } 10 | 11 | label { 12 | font-weight: 600; 13 | font-size: 1.2rem; 14 | line-height: 1.5rem; 15 | letter-spacing: 0.01px; 16 | text-transform: uppercase; 17 | color: $text-color; 18 | } 19 | 20 | input { 21 | background: $neutral-color3; 22 | border: 2px solid $neutral-color2; 23 | border-radius: 6px; 24 | height: 41px; 25 | padding-inline: 1.2rem; 26 | width: 100%; 27 | color: $text-color; 28 | text-transform: uppercase; 29 | transition: all 0.5s ease-in-out; 30 | } 31 | 32 | .flex { 33 | gap: 1.4rem; 34 | 35 | .input-wrapper { 36 | flex: 1; 37 | } 38 | } 39 | 40 | /* From uiverse.io by @menezes11. Converted to Sass and edited by @Luk4x (Github) */ 41 | button { 42 | position: relative; 43 | padding: 1.6rem 1.8rem; 44 | color: $primary-color; 45 | text-transform: uppercase; 46 | letter-spacing: 2px; 47 | overflow: hidden; 48 | transition: 0.2s; 49 | border-radius: 0.5em; 50 | margin-top: 1.6rem; 51 | display: flex; 52 | justify-content: center; 53 | align-items: center; 54 | font-weight: 700; 55 | font-size: 1.4rem; 56 | line-height: 1.7rem; 57 | cursor: not-allowed; 58 | opacity: 0.65; 59 | background: #201214; 60 | height: 49px; 61 | 62 | span { 63 | position: absolute; 64 | 65 | @for $i from 1 through 4 { 66 | &:nth-child(#{$i}) { 67 | @if ($i == 1) { 68 | top: 0; 69 | left: -100%; 70 | width: 100%; 71 | height: 2px; 72 | background: linear-gradient(90deg, transparent, $primary-color); 73 | } @else if ($i == 2) { 74 | top: -100%; 75 | right: 0; 76 | width: 2px; 77 | height: 100%; 78 | background: linear-gradient(180deg, transparent, $primary-color); 79 | } @else if ($i == 3) { 80 | bottom: 0; 81 | right: -100%; 82 | width: 100%; 83 | height: 2px; 84 | background: linear-gradient(90deg, transparent, $secondary-color); 85 | } @else { 86 | bottom: -100%; 87 | left: 0; 88 | width: 2px; 89 | height: 100%; 90 | background: linear-gradient(360deg, transparent, $secondary-color); 91 | } 92 | } 93 | } 94 | } 95 | 96 | &:active { 97 | @for $i from 1 through 4 { 98 | span:nth-child(#{$i}) { 99 | transition: none; 100 | transition-delay: none; 101 | } 102 | } 103 | } 104 | } 105 | 106 | .activeButton { 107 | cursor: pointer; 108 | opacity: 1; 109 | background: $neutral-color3; 110 | 111 | &:hover { 112 | @for $i from 1 through 4 { 113 | span:nth-child(#{$i}) { 114 | @if ($i == 1) { 115 | left: 100%; 116 | transition: 0.7s; 117 | } @else if ($i == 2) { 118 | top: 100%; 119 | transition: 0.7s; 120 | transition-delay: 0.17s; 121 | } @else if ($i == 3) { 122 | right: 100%; 123 | transition: 0.7s; 124 | transition-delay: 0.35s; 125 | } @else { 126 | bottom: 100%; 127 | transition: 0.7s; 128 | transition-delay: 0.52s; 129 | } 130 | } 131 | } 132 | } 133 | 134 | &:active { 135 | background: $dark-primary-color; 136 | background: linear-gradient(to top right, $dark-primary-color 28%, $secondary-color); 137 | color: $text-color; 138 | box-shadow: 0 0 8px $primary-color, 0 0 8px $secondary-color, 0 0 8px $primary-color; 139 | transition: 0.1s; 140 | transition-delay: none; 141 | } 142 | } 143 | 144 | .hideButtonElement { 145 | display: none !important; 146 | } 147 | } 148 | 149 | .waveform { 150 | $uib-size: 25px; 151 | $uib-speed: 1s; 152 | $uib-color: $primary-color; 153 | $uib-line-weight: 3.5px; 154 | 155 | display: flex; 156 | flex-flow: row nowrap; 157 | align-items: center; 158 | justify-content: space-between; 159 | width: $uib-size; 160 | height: calc($uib-size * 0.9); 161 | 162 | .waveform__bar { 163 | width: $uib-line-weight; 164 | height: 100%; 165 | background-color: $uib-color; 166 | 167 | @for $i from 1 through 4 { 168 | &:nth-child(#{$i}) { 169 | @if ($i == 1) { 170 | animation: grow $uib-speed ease-in-out calc($uib-speed * -0.45) infinite; 171 | } @else if ($i == 2) { 172 | animation: grow $uib-speed ease-in-out calc($uib-speed * -0.3) infinite; 173 | } @else if ($i == 3) { 174 | animation: grow $uib-speed ease-in-out calc($uib-speed * -0.15) infinite; 175 | } @else { 176 | animation: grow $uib-speed ease-in-out infinite; 177 | } 178 | } 179 | } 180 | 181 | @keyframes grow { 182 | 0%, 183 | 100% { 184 | transform: scaleY(0.3); 185 | } 186 | 187 | 50% { 188 | transform: scaleY(1); 189 | } 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 || 4 | 🇺🇸 English 5 | | 6 |
| 9 | 🇧🇷 Português 10 | | 11 |
20 | Vídeo | 21 | Tecnologias | 22 | Sobre | 23 | Destaques | 24 | Clone | 25 | Contato 26 |
27 ||
96 |
97 |
123 |
98 | Vitrine.Dev 🪟
99 |
115 |
116 | | :placard: Vitrine.Dev | Lucas Maciel |
117 | | ------------- | --- |
118 | | :sparkles: Nome | **💳 GenCard**
119 | | :label: Tecnologias | sass, regex, imask, sweetalert, nodejs, npm, css-buttons, dom-to-image, vitejs, vanilla-tilt, ui-ball-loaders, javascript, css, html
120 | | :camera: Img |
102 |
103 | |
112 | 106 | 107 | 108 | Lucas Maciel 109 | 110 | 111 | |
124 |
125 | MaykBrito.Dev 🪟
126 |
|
142 |
146 | Voltar ao Topo 147 |
148 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import './css/index.css'; 2 | import IMask from 'imask'; 3 | import swal from 'sweetalert'; 4 | import domtoimage from 'dom-to-image'; 5 | import VanillaTilt from 'vanilla-tilt'; 6 | 7 | // card hover effect 8 | const card = document.querySelector('.cc'); 9 | VanillaTilt.init(card, { 10 | max: 20, 11 | speed: 350, 12 | glare: true 13 | }); 14 | 15 | // Personalizing card 16 | const cardBgColor1 = document.querySelector('.cc-bg svg > g g:nth-child(1) path'); 17 | const cardBgColor2 = document.querySelector('.cc-bg svg > g g:nth-child(2) path'); 18 | const cardLogo = document.querySelector('.cc-logo span:nth-child(2) img'); 19 | 20 | const setCardType = (type, ext) => { 21 | const colors = { 22 | visa: ['#436d99', '#2d57f2'], 23 | mastercard: ['#FC3551', '#c69347'], 24 | maestro: ['#1248FF', '#FC3551'], 25 | discover: ['#F73A67', '#FFC632'], 26 | jcb: ['#2BB01F', '#FE304A'], 27 | unionpay: ['#3BC3EE', '#292D98'], 28 | amex: ['#1238FF', '#E1E1E1'], 29 | diners: ['#8EB5FF', '#0025CE'], 30 | invalid: ['#633bbc', '#601111'], 31 | default: ['#323238', '#323238'] 32 | }; 33 | 34 | cardBgColor1.setAttribute('fill', colors[type][0]); 35 | cardBgColor2.setAttribute('fill', colors[type][1]); 36 | 37 | cardLogo.setAttribute('src', `cc-${type}.${ext ?? 'svg'}`); 38 | const typeCheck = type && type !== 'default'; 39 | cardLogo.style.opacity = typeCheck ? '1' : '0'; 40 | cardLogo.style.transform = typeCheck ? 'translateY(0)' : 'translateY(-5px)'; 41 | }; 42 | globalThis.setCardType = setCardType; 43 | 44 | // Creating masks 45 | const cardNumber = document.getElementById('card-number'); 46 | const cardNumberPattern = { 47 | mask: [ 48 | { 49 | mask: '0000 0000 0000 0000', 50 | regex: /^4\d{0,15}/, 51 | cardType: 'visa' 52 | }, 53 | { 54 | mask: '0000 0000 0000 0000', 55 | regex: /^(5[1-5]\d{0,2}|22[2-9]\d{0,1}|2[3-7]\d{0,2})\d{0,12}/, 56 | cardType: 'mastercard', 57 | ext: 'png' 58 | }, 59 | { 60 | mask: '0000 0000 0000 0000', 61 | regex: /^(?:6011|65\d{0,2}|64[4-9]\d?)\d{0,12}/, 62 | cardType: 'discover', 63 | ext: 'png' 64 | }, 65 | { 66 | mask: '0000 0000 0000 0000', 67 | regex: /^(?:35\d{0,2})\d{0,12}/, 68 | cardType: 'jcb', 69 | ext: 'png' 70 | }, 71 | { 72 | mask: '0000 0000 0000 0000', 73 | regex: /^(?:5[0678]\d{0,2}|6304|67\d{0,2})\d{0,12}/, 74 | cardType: 'maestro', 75 | ext: 'png' 76 | }, 77 | { 78 | mask: '0000 0000 0000 0000', 79 | regex: /^62\d{0,14}/, 80 | cardType: 'unionpay', 81 | ext: 'png' 82 | }, 83 | { 84 | mask: '0000 000000 00000', 85 | regex: /^3[47]\d{0,13}/, 86 | cardType: 'amex', 87 | ext: 'png' 88 | }, 89 | { 90 | mask: '0000 000000 0000', 91 | regex: /^3(?:0([0-5]|9)|[689]\d?)\d{0,11}/, 92 | cardType: 'diners', 93 | ext: 'png' 94 | }, 95 | { 96 | mask: '0000 0000 0000 0000', 97 | cardType: 'default' 98 | } 99 | ], 100 | dispatch: (appended, dynamicMasked) => { 101 | const number = (dynamicMasked.value + appended).replace(/\D/g, ''); 102 | 103 | return dynamicMasked.compiledMasks.find(({ regex }) => number.match(regex)); 104 | } 105 | }; 106 | const cardNumberMasked = IMask(cardNumber, cardNumberPattern); 107 | 108 | const expirationDate = document.getElementById('expiration-date'); 109 | const expirationDatePattern = { 110 | mask: 'MM{/}YY', 111 | blocks: { 112 | MM: { 113 | mask: IMask.MaskedRange, 114 | from: 1, 115 | to: 12 116 | }, 117 | YY: { 118 | mask: IMask.MaskedRange, 119 | from: parseInt(String(new Date().getFullYear()).slice(2)), 120 | to: parseInt(String(new Date().getFullYear()).slice(2)) + 10 121 | } 122 | } 123 | }; 124 | const expirationDateMasked = IMask(expirationDate, expirationDatePattern); 125 | 126 | const securityCode = document.getElementById('security-code'); 127 | const securityCodeMasked = IMask(securityCode, { 128 | mask: '000' 129 | }); 130 | 131 | // Changing card 132 | const cardShowNumber = document.querySelector('.cc-number'); 133 | cardNumberMasked.on('accept', () => { 134 | cardShowNumber.innerText = 135 | cardNumberMasked.value.length > 0 ? cardNumberMasked.value : '#### #### #### ####'; 136 | setCardType( 137 | cardNumberMasked.masked.currentMask.cardType, 138 | cardNumberMasked.masked.currentMask.ext 139 | ); 140 | 141 | if (cardNumberMasked.masked.currentMask.cardType === 'amex') { 142 | cardNumber.style.borderColor = cardNumberMasked.value.length === 17 ? '#633bbc' : '#323238'; 143 | } else if (cardNumberMasked.masked.currentMask.cardType === 'diners') { 144 | cardNumber.style.borderColor = cardNumberMasked.value.length === 16 ? '#633bbc' : '#323238'; 145 | } else { 146 | if (cardNumberMasked.value.length === 19) { 147 | if ( 148 | cardNumberMasked.masked.currentMask.cardType && 149 | cardNumberMasked.masked.currentMask.cardType !== 'default' 150 | ) { 151 | cardNumber.style.borderColor = '#633bbc'; 152 | } else { 153 | cardNumber.style.borderColor = '#601111'; 154 | setCardType('invalid', 'png'); 155 | cardShowNumber.innerText = 'Número Inválido'; 156 | } 157 | } else { 158 | cardNumber.style.borderColor = '#323238'; 159 | } 160 | } 161 | }); 162 | 163 | const cardName = document.getElementById('card-holder'); 164 | const cardShowName = document.querySelector('.cc-holder .value'); 165 | cardName.addEventListener('input', () => { 166 | cardName.value = cardName.value.replace(/[0-9]/g, ''); 167 | cardShowName.innerText = cardName.value.length > 0 ? cardName.value : 'Nome Completo'; 168 | cardName.style.borderColor = cardName.value.match(/[a-zA-Z] [a-zA-Z]/) ? '#633bbc' : '#323238'; 169 | }); 170 | 171 | const cardShowExpirationDate = document.querySelector('.cc-extra .value'); 172 | expirationDateMasked.on('accept', () => { 173 | cardShowExpirationDate.innerText = 174 | expirationDateMasked.value.length > 0 ? expirationDateMasked.value : '##/##'; 175 | expirationDate.style.borderColor = 176 | expirationDateMasked.value.length === 5 ? '#633bbc' : '#323238'; 177 | }); 178 | 179 | const cardShowSecurityCode = document.querySelector('.cc-security .value'); 180 | securityCodeMasked.on('accept', () => { 181 | cardShowSecurityCode.innerText = 182 | securityCodeMasked.value.length > 0 ? securityCodeMasked.value : '###'; 183 | securityCode.style.borderColor = securityCodeMasked.value.length === 3 ? '#633bbc' : '#323238'; 184 | }); 185 | 186 | // form functionality 187 | const inputs = [securityCode, cardName, expirationDate, cardNumber]; 188 | const button = document.getElementById('submitButton'); 189 | const buttonText = document.querySelector('#submitButton p'); 190 | const buttonLoader = document.querySelector('.waveform'); 191 | const buttonImg = document.querySelector('#submitButton img'); 192 | 193 | inputs.forEach(input => { 194 | // on a input in each input, verify if the color of the actual input and others if green (that is, if it is duly filled) and if all inputs are duly filled, unlock button submit 195 | input.addEventListener('input', () => { 196 | const enableButton = inputs.reduce((enable, input) => { 197 | return enable && input.style.borderColor === 'rgb(99, 59, 188)'; 198 | }, true); 199 | 200 | const isInputEmpty = inputs.reduce((isEmpty, input) => { 201 | return isEmpty && input.value.length === 0; 202 | }, true); 203 | 204 | if (enableButton) { 205 | button.classList.add('activeButton'); 206 | button.disabled = false; 207 | 208 | buttonText.classList.remove('hideButtonElement'); 209 | buttonLoader.classList.add('hideButtonElement'); 210 | } else { 211 | if (isInputEmpty) { 212 | buttonLoader.classList.add('hideButtonElement'); 213 | buttonImg.classList.remove('hideButtonElement'); 214 | } else { 215 | buttonLoader.classList.remove('hideButtonElement'); 216 | buttonImg.classList.add('hideButtonElement'); 217 | } 218 | 219 | button.classList.remove('activeButton'); 220 | button.disabled = true; 221 | buttonText.classList.add('hideButtonElement'); 222 | } 223 | }); 224 | }); 225 | 226 | const form = document.querySelector('form'); 227 | form.addEventListener('submit', e => { 228 | e.preventDefault(); 229 | 230 | const formLoader = document.querySelector('.orbit'); 231 | const app = document.getElementById('app'); 232 | 233 | formLoader.classList.add('showOrbit'); 234 | app.classList.add('appLoadingState'); 235 | 236 | setTimeout(async () => { 237 | let userFirstName = ''; 238 | for (let letter of cardName.value) { 239 | if (letter === ' ') break; 240 | else userFirstName += letter; 241 | } 242 | 243 | // generating card image url 244 | const cardUrl = await domtoimage.toPng(card); 245 | 246 | // resetting fields 247 | inputs.forEach(input => { 248 | input.value = ''; 249 | input.style.borderColor = '#323238'; 250 | }); 251 | 252 | cardShowName.innerText = 'NOME COMPLETO'; 253 | cardShowSecurityCode.innerText = '###'; 254 | cardShowNumber.innerText = '#### #### #### ####'; 255 | cardShowExpirationDate.innerText = '##/##'; 256 | setCardType('default'); 257 | 258 | buttonText.classList.add('hideButtonElement'); 259 | buttonImg.classList.remove('hideButtonElement'); 260 | button.classList.remove('activeButton'); 261 | button.disabled = true; 262 | 263 | formLoader.classList.remove('showOrbit'); 264 | app.classList.remove('appLoadingState'); 265 | 266 | swal({ 267 | title: `Hey ${userFirstName.toLowerCase()}!`, 268 | text: 'Seu cartão foi gerado com sucesso!', 269 | icon: 'success', 270 | content: { 271 | element: 'img', 272 | attributes: { 273 | src: cardUrl, 274 | alt: `${userFirstName.toUpperCase()} GC Card image` 275 | } 276 | }, 277 | button: '' 278 | }) 279 | .then(download => { 280 | // generating card download link 281 | if (download) { 282 | const cardImgLink = document.createElement('a'); 283 | cardImgLink.download = `${userFirstName.toUpperCase()}-GC-Card.png`; 284 | cardImgLink.href = cardUrl; 285 | cardImgLink.click(); 286 | } 287 | }) 288 | .catch(error => console.error(error)); 289 | }, 1400); 290 | }); 291 | 292 | // cleaning inputs on load 293 | window.onload = () => { 294 | cardName.value = ''; 295 | securityCode.value = ''; 296 | cardNumber.value = ''; 297 | expirationDate.value = ''; 298 | }; 299 | -------------------------------------------------------------------------------- /src/css/_base.scss: -------------------------------------------------------------------------------- 1 | $primary-color: #8257e6; 2 | $dark-primary-color: #633bbc; 3 | $secondary-color: #42d3ff; 4 | $neutral-color1: #202024; 5 | $neutral-color2: #323238; 6 | $neutral-color3: #121214; 7 | $text-color: #e1e1e6; 8 | 9 | * { 10 | margin: 0; 11 | padding: 0; 12 | box-sizing: border-box; 13 | border: none; 14 | outline: none; 15 | text-decoration: none; 16 | list-style-type: none; 17 | font-family: Inter, sans-serif; 18 | } 19 | 20 | :root { 21 | font-size: 62.5%; 22 | color: #ffffffde; 23 | font-synthesis: none; 24 | text-rendering: optimizeLegibility; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | -webkit-text-size-adjust: 100%; 28 | } 29 | 30 | body { 31 | font-size: 1.6rem; 32 | background: linear-gradient(-5deg, $neutral-color1 51%, $neutral-color2 49%); 33 | display: flex; 34 | place-items: center; 35 | min-height: 100vh; 36 | 37 | .flex { 38 | display: flex; 39 | } 40 | 41 | #app { 42 | display: grid; 43 | justify-content: center; 44 | gap: 3.2rem; 45 | width: 100%; 46 | max-width: 42rem; 47 | margin: 0 auto; 48 | padding: 2.8rem 6rem 4rem; 49 | background: $neutral-color1; 50 | border: 2px solid $neutral-color2; 51 | border-radius: 0.6rem; 52 | position: relative; 53 | z-index: 1; 54 | background: linear-gradient(5deg, $neutral-color2 51%, $neutral-color1 49%); 55 | transition: all 0.35s ease-in-out; 56 | 57 | header { 58 | display: flex; 59 | align-items: center; 60 | border-bottom: 2px solid $dark-primary-color; 61 | width: 148px; 62 | 63 | h1 { 64 | font-size: 24.5px; 65 | margin-left: 8px; 66 | color: $text-color; 67 | font-weight: 300; 68 | } 69 | 70 | span { 71 | font-weight: 400; 72 | color: #6a6a6c; 73 | } 74 | } 75 | } 76 | 77 | .appLoadingState { 78 | filter: brightness(65%) blur(3px) opacity(0.95); 79 | } 80 | 81 | .links { 82 | position: absolute; 83 | bottom: 40px; 84 | left: 50%; 85 | transform: translateX(-50%); 86 | border-bottom: 8px solid $dark-primary-color; 87 | border-radius: 20px; 88 | padding: 0 5px; 89 | 90 | .links-list { 91 | display: flex; 92 | list-style-type: none; 93 | gap: 8px; 94 | 95 | li a img, 96 | button { 97 | background: none; 98 | cursor: pointer; 99 | opacity: 0.7; 100 | transition: all 0.2s ease-in-out; 101 | 102 | &:hover { 103 | opacity: 1; 104 | transform: scale(0.9); 105 | } 106 | } 107 | 108 | button:hover::after { 109 | content: attr(data-tooltip); 110 | position: absolute; 111 | width: 80px; 112 | height: 40px; 113 | background: $secondary-color; 114 | color: $neutral-color3; 115 | font-size: 14.5px; 116 | border-radius: 5px; 117 | border-bottom-left-radius: 50%; 118 | border-bottom-right-radius: 50%; 119 | bottom: 45px; 120 | left: calc(50% - 80px / 2); 121 | line-height: 32px; 122 | transition: all 1s ease-in-out; 123 | } 124 | } 125 | } 126 | 127 | .orbit { 128 | $uib-size: 80px; 129 | $uib-speed: 1.5s; 130 | position: absolute; 131 | display: flex; 132 | align-items: center; 133 | justify-content: center; 134 | height: $uib-size; 135 | width: $uib-size; 136 | top: 45%; 137 | right: 45%; 138 | transform: translate(-50%); 139 | opacity: 0; 140 | 141 | &::before, 142 | &::after { 143 | content: ''; 144 | position: absolute; 145 | height: 60%; 146 | width: 60%; 147 | border-radius: 50%; 148 | will-change: transform; 149 | flex-shrink: 0; 150 | } 151 | 152 | &::before { 153 | background-color: $primary-color; 154 | animation: orbit $uib-speed linear infinite; 155 | } 156 | 157 | &::after { 158 | background-color: $secondary-color; 159 | animation: orbit $uib-speed linear calc($uib-speed / -2) infinite; 160 | } 161 | 162 | @keyframes orbit { 163 | 0% { 164 | transform: translate(calc($uib-size * 0.5)) scale(0.73684); 165 | opacity: 0.65; 166 | } 167 | 5% { 168 | transform: translate(calc($uib-size * 0.4)) scale(0.684208); 169 | opacity: 0.58; 170 | } 171 | 10% { 172 | transform: translate(calc($uib-size * 0.3)) scale(0.631576); 173 | opacity: 0.51; 174 | } 175 | 15% { 176 | transform: translate(calc($uib-size * 0.2)) scale(0.578944); 177 | opacity: 0.44; 178 | } 179 | 20% { 180 | transform: translate(calc($uib-size * 0.1)) scale(0.526312); 181 | opacity: 0.37; 182 | } 183 | 25% { 184 | transform: translate(0%) scale(0.47368); 185 | opacity: 0.3; 186 | } 187 | 30% { 188 | transform: translate(calc($uib-size * -0.1)) scale(0.526312); 189 | opacity: 0.37; 190 | } 191 | 35% { 192 | transform: translate(calc($uib-size * -0.2)) scale(0.578944); 193 | opacity: 0.44; 194 | } 195 | 40% { 196 | transform: translate(calc($uib-size * -0.3)) scale(0.631576); 197 | opacity: 0.51; 198 | } 199 | 45% { 200 | transform: translate(calc($uib-size * -0.4)) scale(0.684208); 201 | opacity: 0.58; 202 | } 203 | 50% { 204 | transform: translate(calc($uib-size * -0.5)) scale(0.73684); 205 | opacity: 0.65; 206 | } 207 | 55% { 208 | transform: translate(calc($uib-size * -0.4)) scale(0.789472); 209 | opacity: 0.72; 210 | } 211 | 60% { 212 | transform: translate(calc($uib-size * -0.3)) scale(0.842104); 213 | opacity: 0.79; 214 | } 215 | 65% { 216 | transform: translate(calc($uib-size * -0.2)) scale(0.894736); 217 | opacity: 0.86; 218 | } 219 | 70% { 220 | transform: translate(calc($uib-size * -0.1)) scale(0.947368); 221 | opacity: 0.93; 222 | } 223 | 75% { 224 | transform: translate(0%) scale(1); 225 | opacity: 1; 226 | } 227 | 80% { 228 | transform: translate(calc($uib-size * 0.1)) scale(0.947368); 229 | opacity: 0.93; 230 | } 231 | 85% { 232 | transform: translate(calc($uib-size * 0.2)) scale(0.894736); 233 | opacity: 0.86; 234 | } 235 | 90% { 236 | transform: translate(calc($uib-size * 0.3)) scale(0.842104); 237 | opacity: 0.79; 238 | } 239 | 95% { 240 | transform: translate(calc($uib-size * 0.4)) scale(0.789472); 241 | opacity: 0.72; 242 | } 243 | 100% { 244 | transform: translate(calc($uib-size * 0.5)) scale(0.73684); 245 | opacity: 0.65; 246 | } 247 | } 248 | } 249 | 250 | .showOrbit { 251 | transition: all 0.8s ease-in-out; 252 | opacity: 1; 253 | z-index: 1; 254 | } 255 | 256 | .squares { 257 | position: absolute; 258 | top: 0; 259 | left: 0; 260 | width: 100vw; 261 | height: 100vh; 262 | overflow: hidden; 263 | 264 | li { 265 | position: absolute; 266 | display: block; 267 | list-style: none; 268 | width: 20px; 269 | height: 20px; 270 | background: #42d3ff60; 271 | animation: animate 25s linear infinite; 272 | bottom: -150px; 273 | border-radius: 6%; 274 | 275 | @for $i from 1 through 10 { 276 | &:nth-child(#{$i}) { 277 | @if ($i == 1) { 278 | left: 25%; 279 | width: 80px; 280 | height: 80px; 281 | animation-delay: 0s; 282 | } @else if ($i == 2) { 283 | left: 10%; 284 | width: 20px; 285 | height: 20px; 286 | animation-delay: 2s; 287 | animation-duration: 12s; 288 | } @else if ($i == 3) { 289 | left: 70%; 290 | width: 20px; 291 | height: 20px; 292 | animation-delay: 4s; 293 | } @else if ($i == 4) { 294 | left: 40%; 295 | width: 60px; 296 | height: 60px; 297 | animation-delay: 0s; 298 | animation-duration: 18s; 299 | } @else if ($i == 5) { 300 | left: 65%; 301 | width: 20px; 302 | height: 20px; 303 | animation-delay: 0s; 304 | } @else if ($i == 6) { 305 | left: 75%; 306 | width: 110px; 307 | height: 110px; 308 | animation-delay: 3s; 309 | } @else if ($i == 7) { 310 | left: 35%; 311 | width: 150px; 312 | height: 150px; 313 | animation-delay: 7s; 314 | } @else if ($i == 8) { 315 | left: 50%; 316 | width: 25px; 317 | height: 25px; 318 | animation-delay: 15s; 319 | animation-duration: 45s; 320 | } @else if ($i == 9) { 321 | left: 20%; 322 | width: 15px; 323 | height: 15px; 324 | animation-delay: 2s; 325 | animation-duration: 35s; 326 | } @else { 327 | left: 85%; 328 | width: 150px; 329 | height: 150px; 330 | animation-delay: 0s; 331 | animation-duration: 11s; 332 | } 333 | } 334 | } 335 | 336 | @keyframes animate { 337 | 0% { 338 | transform: translateY(0) rotate(0deg); 339 | opacity: 1; 340 | } 341 | 342 | 100% { 343 | transform: translateY(-120vh) rotate(720deg); 344 | opacity: 0; 345 | } 346 | } 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 16 | 20 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 |
45 |