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.
Note: Delete this note and update the table of contents based on what sections you keep.
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
Add a screenshot of your solution. The easiest way to do this is to use Firefox to view your project, right-click the page and select "Take a Screenshot". You can choose either a full-height screenshot or a cropped one based on how long the page is. If it's very long, it might be best to crop it.
Alternatively, you can use a tool like FireShot to take the screenshot. FireShot has a free option, so you don't need to purchase it.
Then crop/optimize/edit your image however you like, add it to your project, and update the file path in the image above.
Note: Delete this note and the paragraphs above when you add your screenshot. If you prefer not to add a screenshot, feel free to remove this entire section.
- Solution URL: Add solution URL here
- Live Site URL: Add live site URL here
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- React - JS library
- Next.js - React framework
- Styled Components - For styles
Note: These are just examples. Delete this note and replace the list above with your own choices
-
<form action="#" novalidate>
This ensures that the form does not submit anywhere and does not perform any validation. -
The
element is a convenient way to create groups of widgets that share the same purpose, for styling and semantic purposes. You can label a by including a element just below the opening tag. -
The HTML element is a container element into which a site or app can inject the results of a calculation or the outcome of a user action.
const form = document.querySelector(".tip-calculator") as HTMLFormElement;
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// {billAmount: '', tip-percentage: '5', numberOfPeople: ''}
- I tried to hide the radio button, while I am using grid for my layout,
display: none
on the.select-tip__radio[type="radio"]
seems to work, but lost the accessibility, so below is better
.select-tip__radio[type="radio"] {
position: absolute;
opacity: 0;
pointer-events: none;
}
- When changing checked color of the label, need to select checked on the radio button
input[type="radio"]:checked + label {
background-color: var(--color-button-tip-selected);
color: var(--color-button-tip-text-selected);
}
- I put a input inside the radio button label, and put it to disabled and use CSS to set
pointer-events: none
. So user can select the custom radio label just as the other.
<label class="select-tip__radio-label select-tip__radio-label--custom" for="tip-percentage-custom">
<input
type="number"
class="select-tip__radio-custom-input"
name="tip-percentage-custom-input"
name="other_tip"
placeholder="Custom"
min="0"
disabled
/></label>
- When the user selected the custom radio label I use JS to set the input
disabled = false
, setpointer-events: auto
and focus on the input.
if (target.id === "tip-percentage-custom") {
customInput.disabled = false;
customInput.style.pointerEvents = "auto";
customInput.focus();
} else {
customInput.disabled = true;
customInput.style.pointerEvents = "none";
customInput.value = "";
}
- In TypeScript, the Record<K, T> utility type is used to define an object type with a set of keys K and a set of values T. It essentially represents an object where every key has a specific value type.
-
toFixed() will also not round correctly in some cases, also it return a string.
parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56. parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56. // However, it will return correct result if you round 1.5551. parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.
-
using the Math.round() function, problem it will not leave 2 decimal place.
var a = 5.678948;
let b = 10.257683;
let c = 6.6456583;
let result1 = Math.round(a*100)/100;
let result2 = Math.round(b*10)/10;
let result3 = Math.round(x*1000)/1000;
console.log(result1) // 5.68
console.log(result2) // 10.3
console.log(typeof result2) // 6.646
- use
aria-live="polite"
for displaying error
- improve the accessibility on the screen reader reading the Tip Amount per person and the result.
- How to structure a web form - Learn how to use with radio input. Learn how to use
, for radio button to build a simple payment form with good html semantic elements. - How to stop form submission using JavaScript?
- Client-side form validation
-how to create a custom Input radio button which have attributes inside it - show me basic, how to style an input
- CodePen Home Radio button without the dot - a simple example
- Pure CSS - SVG Radio Selector Buttons - advance example
- Styling Radio Buttons with CSS (59 Custom Examples) - 59 different examples
- Custom Styling Radio Buttons: The Modern Way
- How TO - Custom Checkbox
- HTML Forms: Radio buttons with text fields - How to add custom tips in radio button
- Design your service using GOV.UK styles, components and patterns - Use this design system to make government services consistent with GOV.UK. Learn from the research and experience of other service teams and avoid repeating work that’s already been done.
- Website - Add your name here
- Frontend Mentor - @yourusername
- Twitter - @yourusername
Note: Delete this note and add/remove/edit lines above based on what links you'd like to share.
This is where you can give a hat tip to anyone who helped you out on this project. Perhaps you worked in a team or got some inspiration from someone else's solution. This is the perfect place to give them some credit.
Note: Delete this note and edit this section's content as necessary. If you completed this challenge by yourself, feel free to delete this section entirely.