Skip to content

GeorgeDaris/tip-calculator

Repository files navigation

Frontend Mentor - Tip calculator app solution

This is a solution to the Tip calculator app challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

Table of contents

Overview

The challenge

Users should be able to:

  • View the optimal layout for the app depending on their device's screen size
  • See hover states for all interactive elements on the page
  • Calculate the correct tip and total cost of the bill per person

Screenshot

Links

My process

Built with

  • Semantic HTML5 markup
  • Vue 3 (Composition API)
  • Typescript
  • Tailwind CSS

What I learned

I used this project to learn Typescript, which I had been meaning to learn for a while. As someone who has some experience with typed languages, it was interesting to bring the safety it offers to a web project. This app isn't complex enough to need this kind of safety, but I was surprised to see how much more readable it made the code.

Take this as an example:

const splitTitle = (word: string, splitAt: number[]) => {
  const first = word.slice(splitAt[0], splitAt[1])
  const last = word.slice(splitAt[1])
  return `${first} <br /> ${last}`
}

splitTitle('Splitter', [0, 4])
// returns:
//spli
//tter

This function splits a word into two parts based on the specified place, and prints it out in two lines. Using Typescript we can declare the types of the parameters, making the function easier to understand.

Types can be specified through inference, meaning that this variable will have a type of Array without having to be explicitly declared: const tipAmounts = reactive([5, 10, 15, 25, 50]).

Vue with Typescript

When using Vue with Typescript, I had difficulty working with otherwise simple functionality at first. One has to be more explicit when declaring props, since types obviously matter. A variable of type number can't be used as an input's name, since it is a number. To use it properly, string interpolation can be used to pass the value as a string:

<input
    type="number"
    :name="`${amount}`"
    :id="`amount-${amount}` />

Another thing I learned is that it is better to use strings as the first part of an id when working with inputs.

The object syntax is also useful for declaring props with types and default values, as opposed to the simpler array syntax I had been using in the past:

defineProps({
  amount: {
    type: Number,
    default: 0
  }
})

Accessibility

Despite the simplicity of this project, I found many solutions which had accessibility issues due to a lack of semantic markup and missing label and input attributes. It should be noted that the design itself isn't accessible to users with vision impairment, as a quick look at the accessibility tab of the dev tools will show that certain text leafs don't have sufficient contrast.

While creating the NumberInput component I made use of semantic markup and learned some best practices along the way to make it reusable and accessible. The input can access a name and an id based on props to bind it with the label, and an optional placeholder can also be specified.

From design to code

It was interesting to create something based on an existing design again while making sure to keep it responsive and as close as possible to the original design. I used the colours provided by the design file to extend the Tailwind theme and use them across the app. Flexbox and Grid were also used to layout the contents of the app.

Continued development

Now that I have gotten down the basics of Typescript, I would like to focus on a more complex project where union types and interfaces could be used, among more advanced concepts.

Useful resources

Author

Acknowledgments

I would like to thank the folks over at the VueLand Discord server, who were kind enough to help me out with questions regarding Typescript with Vue.

About

Frontendmentor challenge

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published