├── design ├── active-states.jpg ├── mobile-design.jpg ├── desktop-preview.jpg ├── desktop-design-empty.jpg └── desktop-design-completed.jpg ├── images ├── favicon-32x32.png ├── Screenshot 2023-01-27 at 01-12-08 Tip calculator app.png ├── icon-person.svg ├── icon-dollar.svg └── logo.svg ├── assests └── fonts │ ├── SpaceMono-Bold.ttf │ └── SpaceMono-Regular.ttf ├── .gitignore ├── style-guide.md ├── README.md ├── index.html ├── script.js └── styles.css /design/active-states.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/design/active-states.jpg -------------------------------------------------------------------------------- /design/mobile-design.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/design/mobile-design.jpg -------------------------------------------------------------------------------- /images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/images/favicon-32x32.png -------------------------------------------------------------------------------- /design/desktop-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/design/desktop-preview.jpg -------------------------------------------------------------------------------- /assests/fonts/SpaceMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/assests/fonts/SpaceMono-Bold.ttf -------------------------------------------------------------------------------- /design/desktop-design-empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/design/desktop-design-empty.jpg -------------------------------------------------------------------------------- /assests/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/assests/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /design/desktop-design-completed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/design/desktop-design-completed.jpg -------------------------------------------------------------------------------- /images/Screenshot 2023-01-27 at 01-12-08 Tip calculator app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikennarichard/Tip-calculator-app/HEAD/images/Screenshot 2023-01-27 at 01-12-08 Tip calculator app.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Avoid accidental upload of the Sketch and Figma design files 2 | ##################################################### 3 | ## Please do not remove lines 5 and 6 - thanks! 🙂 ## 4 | ##################################################### 5 | *.sketch 6 | *.fig 7 | 8 | # Avoid accidental XD upload if you convert the design file 9 | ############################################### 10 | ## Please do not remove line 12 - thanks! 🙂 ## 11 | ############################################### 12 | *.xd 13 | 14 | # Avoid your project being littered with annoying .DS_Store files! 15 | .DS_Store 16 | .prettierignore -------------------------------------------------------------------------------- /style-guide.md: -------------------------------------------------------------------------------- 1 | # Front-end Style Guide 2 | 3 | ## Layout 4 | 5 | The designs were created to the following widths: 6 | 7 | - Mobile: 375px 8 | - Desktop: 1440px 9 | 10 | ## Colors 11 | 12 | ### Primary 13 | 14 | - Strong cyan: hsl(172, 67%, 45%) 15 | 16 | ### Neutral 17 | 18 | - Very dark cyan: hsl(183, 100%, 15%) 19 | - Dark grayish cyan: hsl(186, 14%, 43%) 20 | - Grayish cyan: hsl(184, 14%, 56%) 21 | - Light grayish cyan: hsl(185, 41%, 84%) 22 | - Very light grayish cyan: hsl(189, 41%, 97%) 23 | - White: hsl(0, 0%, 100%) 24 | 25 | ## Typography 26 | 27 | ### Body Copy 28 | 29 | - Font size (form inputs): 24px 30 | 31 | ### Font 32 | 33 | - Family: [Space Mono](https://fonts.google.com/specimen/Space+Mono) 34 | - Weights: 700 35 | -------------------------------------------------------------------------------- /images/icon-person.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/icon-dollar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontend Mentor - Tip calculator app solution 2 | 3 | This is a solution to the [Tip calculator app challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/tip-calculator-app-ugJNGbJUX). Frontend Mentor challenges help you improve your coding skills by building realistic projects. 4 | 5 | - [app](https://ikennarichard.github.io/Tip-calculator-app/) 6 | 7 | ## Table of contents 8 | 9 | - [Overview](#overview) 10 | - [The challenge](#the-challenge) 11 | - [Screenshot](#screenshot) 12 | - [Links](#links) 13 | - [Built with](#built-with) 14 | 15 | - [Author](#author) 16 | 17 | ## Overview 18 | 19 | ### The challenge 20 | 21 | Users should be able to: 22 | 23 | - View the optimal layout for the app depending on their device's screen size 24 | - See hover states for all interactive elements on the page 25 | - Calculate the correct tip and total cost of the bill per person 26 | 27 | ### Screenshot 28 | 29 | ![screenshot](./images/Screenshot%202023-01-27%20at%2001-12-08%20Tip%20calculator%20app.png) 30 | 31 | 32 | ### Links 33 | 34 | - [github](https://github.com/ikennarichard/Tip-calculator-app) 35 | 36 | 37 | 38 | ## My process 39 | 40 | ### Built with 41 | 42 | - Semantic HTML5 markup 43 | - CSS custom properties 44 | - Flexbox 45 | - CSS Grid 46 | - Mobile-first workflow 47 | 48 | 49 | ## Author 50 | 51 | - Website - [ikennarichard](https://www.github.com/ikennarichard) 52 | - Frontend Mentor - [@ikennarichard](https://www.frontendmentor.io/profile/yourusername) 53 | - Twitter - [@ikennarichard_](https://www.twitter.com/@ikennarichard_) 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Tip calculator app 9 | 10 | 11 |
12 |
13 |

SPLITTER

14 |
15 |
16 |
17 |
18 | 19 | dollar sign 20 | 21 |
22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 |
37 | 38 | person icon 39 | 40 | 41 | 42 | 43 | 44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 |
52 |

Tip Amount

53 | / person 54 |
55 |
56 | $0.00 57 |
58 |
59 | 60 |
61 | 62 |
63 |

Total

64 | / person 65 |
66 |
67 | $0.00 68 |
69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | // dom elements 2 | const bill = document.querySelector("#bill"); 3 | 4 | const tips = [...document.querySelectorAll(".select_btn")]; 5 | 6 | const numberFields = [...document.querySelectorAll("input[type=number]")]; 7 | 8 | const tipAmount = document.querySelector(".tip_amount > strong"); 9 | 10 | const totalTip = document.querySelector(".total_tip > strong"); 11 | 12 | const resetBtn = document.querySelector(".reset"); 13 | 14 | 15 | 16 | 17 | // global variables 18 | let tipValue; 19 | 20 | 21 | // bill 22 | let billAmount = () => bill.valueAsNumber; 23 | 24 | // get tip 25 | tips.forEach((tip) => { 26 | 27 | tip.addEventListener("click", (e) => { 28 | toggleStyle(e); 29 | document.querySelector(".custom_tip").value = ""; 30 | tipValue = e.target.value.slice(0, -1); 31 | 32 | let noOfPeople = document.querySelector("#no_of_people").valueAsNumber; 33 | 34 | 35 | let eachPersonsBill = billAmount() / noOfPeople; 36 | 37 | let tip_amount = eachPersonsBill * (Number(tipValue) / 100); 38 | 39 | let total = eachPersonsBill + tip_amount; 40 | 41 | if (!tip_amount){ 42 | tipAmount.textContent = `$0.00`; 43 | totalTip.textContent = `$0.00`; 44 | } else { 45 | tipAmount.textContent = `$${tip_amount.toFixed(2)}`; 46 | totalTip.textContent = `$${total.toFixed(2)}`; 47 | } 48 | 49 | } 50 | )}); 51 | 52 | 53 | 54 | numberFields.forEach((field) => { 55 | field.addEventListener("input", (e) => { 56 | 57 | if (e.target.value == "") { 58 | e.target.classList.remove("no_error"); 59 | e.target.classList.add("error"); 60 | 61 | 62 | } else { 63 | e.target.classList.add("no_error"); 64 | 65 | } 66 | 67 | if (field['id'] == "no_of_people"){ 68 | // display error message 69 | let errorMessage = document.querySelector(".error_message"); 70 | 71 | if(field.value == "") { 72 | 73 | errorMessage.style.visibility = "visible"; 74 | } else { 75 | errorMessage.style.visibility = "hidden"; 76 | } 77 | 78 | 79 | // calculate tip 80 | let eachPersonsBill = billAmount() / e.target.valueAsNumber; 81 | 82 | let tip_amount = eachPersonsBill * (Number(tipValue) / 100); 83 | 84 | 85 | 86 | 87 | let total = eachPersonsBill + tip_amount; 88 | 89 | if (!tip_amount){ 90 | tipAmount.textContent = `$0.00`; 91 | totalTip.textContent = `$0.00`; 92 | } else { 93 | tipAmount.textContent = `$${tip_amount.toFixed(2)}`;; 94 | totalTip.textContent = `$${total.toFixed(2)}`; 95 | } 96 | 97 | 98 | } 99 | 100 | if (field['id'] === "select_tip") { 101 | 102 | tipValue = document.querySelector(".custom_tip").valueAsNumber; 103 | 104 | let noOfPeople = document.querySelector("#no_of_people").valueAsNumber; 105 | 106 | 107 | let eachPersonsBill = billAmount() / noOfPeople; 108 | 109 | 110 | let tip_amount = eachPersonsBill * (Number(tipValue) / 100); 111 | 112 | let total = eachPersonsBill + tip_amount; 113 | 114 | if (!tip_amount){ 115 | tipAmount.textContent = `$0.00`; 116 | totalTip.textContent = `$0.00`; 117 | } else { 118 | tipAmount.textContent = `$${tip_amount.toFixed(2)}`;; 119 | totalTip.textContent = `$${total.toFixed(2)}`; 120 | } 121 | } 122 | }); 123 | }); 124 | 125 | 126 | //toggle button background 127 | function toggleStyle(event) { 128 | 129 | tips.forEach((tip) => { 130 | if (tip == event.target) { 131 | tip.classList.add("button_color"); 132 | } else { 133 | tip.classList.remove("button_color"); 134 | } 135 | }); 136 | 137 | }; 138 | 139 | 140 | //remove style when field is clicked 141 | document.querySelector(".custom_tip").addEventListener("click", ()=>{ 142 | tips.forEach(tip => { 143 | tip.classList.remove("button_color"); 144 | }) 145 | }) 146 | 147 | 148 | // reset button 149 | resetBtn.addEventListener ("click", ()=> { 150 | 151 | bill.value = ""; 152 | document.querySelector(".no_of_people").value = ""; 153 | document.querySelector(".custom_tip").value = ""; 154 | window.location.reload(); 155 | }) 156 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* font */ 2 | @font-face { 3 | font-family: --space-bold; 4 | src: url(./assests/fonts/SpaceMono-Bold.ttf); 5 | font-weight: 700; 6 | } 7 | 8 | @font-face { 9 | font-family: --space-regular; 10 | src: url(./assests/fonts/SpaceMono-Regular.ttf); 11 | } 12 | 13 | /* styles */ 14 | :root { 15 | --strong-cyan: hsl(172, 67%, 45%); 16 | --very-dark-cyan: hsl(183, 100%, 15%); 17 | --dark-grayish-cyan: hsl(186, 14%, 43%); 18 | --grayish-cyan: hsl(184, 14%, 56%); 19 | --light-grayish-cyan: hsl(185, 41%, 84%); 20 | --very-light-grayish-cyan: hsl(189, 41%, 97%); 21 | --white: hsl(0, 0%, 100%); 22 | --soft-red: #F47174; 23 | } 24 | 25 | 26 | * { 27 | box-sizing: border-box; 28 | margin: 0; 29 | padding: 0; 30 | line-height: 1.5; 31 | } 32 | 33 | 34 | html { 35 | font-size: 62.5%; 36 | } 37 | 38 | 39 | img { 40 | max-width: 100%; 41 | } 42 | 43 | 44 | 45 | body { 46 | display: grid; 47 | place-items: center; 48 | min-height: 100vh; 49 | font-family: --space-regular, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, monospace; 50 | font-weight: 700; 51 | background-color: var(--light-grayish-cyan); 52 | } 53 | 54 | 55 | h1 { 56 | text-align: center; 57 | font-family: --space-bold; 58 | /* border: 1px solid; */ 59 | word-wrap: break-word; 60 | width: 3.8em; 61 | letter-spacing: 5px; 62 | color: var(--dark-grayish-cyan); 63 | } 64 | 65 | 66 | input { 67 | font-family: --space-bold; 68 | width: 100%; 69 | } 70 | 71 | 72 | 73 | .wrapper { 74 | display: grid; 75 | place-items: center; 76 | width: 100%; 77 | 78 | gap: 20px; 79 | padding-block-start: 4em; 80 | /* border: 5px solid burlywood; */ 81 | } 82 | 83 | 84 | main { 85 | padding-block-start: 2em; 86 | width: 100%; 87 | min-width: 228px; 88 | 89 | display: grid; 90 | place-items: center; 91 | background-color: var(--white); 92 | padding: 2em 1.5em; 93 | gap: 20px; 94 | border-top-left-radius: 15px; 95 | border-top-right-radius: 15px; 96 | } 97 | 98 | 99 | .calculator { 100 | width: 100%; 101 | display: flex; 102 | flex-direction: column; 103 | align-items: center; 104 | gap: 20px; 105 | } 106 | 107 | 108 | 109 | .no_of_people, .bill, .custom_tip { 110 | display: block; 111 | text-align: right; 112 | padding-inline-end: 9px; 113 | border: none; 114 | outline: none; 115 | padding-block: 5px; 116 | border-radius: 3px; 117 | background-color: var(--very-light-grayish-cyan); 118 | color: var(--very-dark-cyan); 119 | appearance: textfield; 120 | } 121 | 122 | 123 | .bill, .no_of_people { 124 | margin-block-start: .7em; 125 | } 126 | 127 | .error { 128 | outline: 2px solid var(--soft-red); 129 | } 130 | 131 | .no_error { 132 | outline: 1px solid var(--strong-cyan); 133 | } 134 | 135 | 136 | .calculator > div { 137 | width: 100%; 138 | } 139 | 140 | 141 | 142 | .dollar_sign { 143 | height: 12px; 144 | position: relative; 145 | top: 34px; 146 | right: 25px; 147 | } 148 | 149 | 150 | 151 | .tip_percent { 152 | display: grid; 153 | grid-template-columns: repeat(autofit); 154 | gap: 10px; 155 | width: 100%; 156 | } 157 | 158 | 159 | label { 160 | letter-spacing: 1px; 161 | } 162 | 163 | .tip_percent > label{ 164 | grid-column: span 2; 165 | } 166 | 167 | 168 | .select_btn { 169 | border: none; 170 | outline: none; 171 | padding: .4em 2em; 172 | background-color: var(--very-dark-cyan); 173 | color: var(--very-light-grayish-cyan); 174 | cursor: pointer; 175 | border-radius: 4px; 176 | transition: background-color, 0.2s ease-in-out; 177 | } 178 | 179 | 180 | 181 | input[type=button]:hover { 182 | background-color: var(--strong-cyan); 183 | color: var(--very-dark-cyan); 184 | } 185 | 186 | 187 | .button_color { 188 | background-color: var(--strong-cyan); 189 | color: var(--very-dark-cyan); 190 | } 191 | 192 | .num_people { 193 | position: relative; 194 | } 195 | 196 | 197 | .error_message { 198 | word-spacing: 2px; 199 | position: absolute; 200 | right: 0; 201 | visibility: hidden; 202 | color: var(--soft-red) ; 203 | } 204 | 205 | 206 | 207 | .figure_icon { 208 | position: relative; 209 | top: 34px; 210 | right: 110px; 211 | height: 12px; 212 | } 213 | 214 | 215 | .total_tip_section { 216 | background-color: var(--very-dark-cyan); 217 | display: flex; 218 | flex-direction: column; 219 | gap: 20px; 220 | width: 100%; 221 | padding: 1.4em 1.5em; 222 | border-radius: 1em; 223 | } 224 | 225 | .view { 226 | display: flex; 227 | flex-direction: column; 228 | gap: 15px; 229 | } 230 | 231 | 232 | .tip, .total { 233 | display: flex; 234 | justify-content: space-between; 235 | align-items: center; 236 | flex-wrap: wrap; 237 | } 238 | 239 | 240 | .tip_person, .total_person { 241 | color: var(--light-grayish-cyan) ; 242 | } 243 | 244 | .tip_person > span, .total_person > span { 245 | color: var(--dark-grayish-cyan) 246 | } 247 | 248 | strong { 249 | font-size: 2.5rem; 250 | color: var(--strong-cyan); 251 | font-family: --space-bold; 252 | } 253 | 254 | 255 | button { 256 | background-color: var(--strong-cyan); 257 | outline: none; 258 | border: none; 259 | color: var(--very-dark-cyan); 260 | font-weight: 700; 261 | padding: .5em 4em; 262 | border-radius: .4em; 263 | 264 | } 265 | 266 | button:hover { 267 | cursor: pointer; 268 | filter: brightness(150%); 269 | } 270 | 271 | 272 | @media (min-width: 550px) { 273 | 274 | .wrapper { 275 | max-width: 620px 276 | } 277 | 278 | 279 | main { 280 | grid-template-columns: 1fr 1fr; 281 | margin-block-start: 25px; 282 | } 283 | 284 | .tip_percent { 285 | grid-template-columns: repeat(3, 1fr); 286 | } 287 | 288 | .tip_percent > label{ 289 | grid-column: span 3; 290 | } 291 | 292 | .total_tip_section { 293 | overflow: hidden; 294 | text-overflow: ellipsis; 295 | white-space: nowrap; 296 | height: 100%; 297 | justify-content: space-between; 298 | } 299 | 300 | .view { 301 | padding-top: 1.5em; 302 | gap: 27px; 303 | } 304 | 305 | strong { 306 | font-size: 3rem; 307 | } 308 | 309 | button { 310 | margin-block-end: 10px; 311 | } 312 | } --------------------------------------------------------------------------------