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.
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
- Solution URL: GitHub
- Live Site URL: GitHub Pages
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- A
landmark
is an important subsection of the page. - A
landmark
role is an abstract role for a section of content that is important enough for users to want to be able to navigate to it quickly and easily. - Landmarks help assistive technologies in easy navigation and content finding.
- To create a landmark role you should use semantic elements.
- You mustn't use
role=landmark
.
ARIA: landmark role article on MDN explaining what a landmark and landmark role are and also listing best practices related to these two
- The
<form>
element defines aform
landmark when it has an accessible name. - An accessible name is a short string that is associated with an element and serves as a label for it. It describes its purpose and distinguishes it from other elements.
- One way to create an accessible name is to make use of attributes such as
aria-label
,aria-labelledby
ortitle
. For the tip calculator I usedaria-labelledby
attribute. I've put aheader
element withh1
heading inside theform
and wrapped the rest of content in a div:
<form action="#" autocomplete="off" class="tip-calculator container" aria-labelledby="form-title">
<header class="center">
<h1 class="sr-only">Splitter, the tip calculator</h1>
<div class="logo" aria-hidden="true"></div>
</header>
<div class="tip-calculator__wrapper"></div>
</form
ARIA: form role - an MDN article about the form role Providing Accessible Names and Descriptions - explanation of accessible names and descriptions and instructions on how to use them
The design of the calculator includes an SVG with a wordmark for the calculator. Since the image is the name itself I decided to hide it for screen readers with aria-hidden
attribute. This way the name "Splitter" is provided to screen reader users in the heading,which is visually hidden, and in the SVG for sighted users.
Grouping related controls makes the form easier to understand for the users, as they can focus on smaller chunks of it. Groups of controls can be created in two ways:
- with the use of
<fieldset>
and<legend>
elements - through associating related controls with WAI-ARIA
I chose the first option.
<fieldset>
is the container that holds related controls together<legend>
functions as a heading for the whole group
Grouping Controls - explanation on how to group related form controls
<fieldset>
and <legend>
elements have some styling by default. The way they normally look doesn't match the design. Therefore these default styles had to be removed:
legend {
padding: 0;
display: table;
}
fieldset {
border: 0;
padding: 0.01rem 0 0 0;
margin: 0;
min-width: 0;
}
body:not(:-moz-handler-blocked) fieldset {
display: table-cell;
}
Reset your fieldset - article explaining how to remove default styling of <fieldset>
and <legend>
elements
The number
type inputs are to be used for numbers that are supposed to be incremented. Hence browsers usually display arrows for increasing and decreasing the value.
This is not the case for the tip calculator and so I decided to use <input>
with the type of text
and inputmode
attribute:
<input type="text" inputmode="numeric" pattern="\d*" />
UPDATE
Changed the inputs to <input type="number">
and removed the spinners.
For Firefox:
input[type="number"] {
appearance: textfield;
}
For Chrome, Safari, Edge and Opera:
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
Math.round()
function rounds the number to the nearest integer and returns that value.toFixed()
function converts the number to a string and keeps the specified number of decimal places. The number of decimal places you want is the parameter of the function. This parameter is optional and if omitted it is assumed it's0
.parseFloat()
converts a string to a floating point number
When rounding numbers in JavaScript it is a good idea to use exponential notation, which lets you avoid any rounding errors:
const numberToRound = 4.2765;
const rounded = parseFloat(Math.round(numberToRound + "e2") - "e-2");
Rounding Decimals in JavaScript - short article explaining how to round decimal numbers in JavaScript How to Round to a Certain Number of Decimal Places in JavaScript - more detailed article on rounding based on the one provided above
- First you need to indicate that the input is invalid. You can do so with the
aria-invalid
attribute, which you add to the field with incorrect value. Once the input is corrected, the attribute needs to be removed - Fields with
aria-invalid
attribute are read out as being invalid - You can use
aria-invalid
to style the incorrect field appropriately:
input[aria-invalid="true"] {
color: red;
}
- This is self explanatory. You can use text color, borders, backgrounds, icons, etc.
- As noted above you can make use of
aria-invalid
attribute when styling
Use attributes on on the div
containing the error message and present in HTML on page load:
role="alert"
aria-live="assertive"
combined witharia-relevant="additions removlas"
, you may also want to usearia-atomic="true"
(if you want the element to be treated as a whole) Bothrole="alert"
andaria-live
change the element into a live region which means changes to it will be read out.aria-alert
does the same thing asaria-live="assertive"
+aria-atomic="true"
How to make inline error messages accessible - article explaining techniques you can use to ensure error messages are accessible ARIA live regions - an MDN article with detailed information about live regions and different attributes you can use
- Forms
- Accessibility
- JavaScript
- ARIA: landmark role article on MDN explaining what a landmark and landmark role are and also listing best practices related to these two
- ARIA: form role - an MDN article about the form role
- Providing Accessible Names and Descriptions - explanation of accessible names and descriptions and instructions on how to use them
- Grouping Controls - explanation on how to group related form controls
- Reset your fieldset - article explaining how to remove default styling of
<fieldset>
and<legend>
elements - Rounding Decimals in JavaScript - short article explaining how to round decimal numbers in JavaScript
- How to Round to a Certain Number of Decimal Places in JavaScript - more detailed article on rounding based on the one provided above
- How to make inline error messages accessible - article explaining techniques you can use to ensure error messages are accessible
- ARIA live regions - an MDN article with detailed information about live regions and different attributes you can use
- Frontend Mentor - @ania221B
Shout out to Victor who shared feedback on inputs😊