Sometimes we hold so much in our projects that we forget to see what's going on in the rest of the world. As developers, especially as full stack developers, it's becoming increasingly challenging to stay up to date with every detail of technology. I feel that everything is becoming a specialized skill. The breadth of knowledge you need to have to call yourself, let's say, a front-end developer is quite expansive and specialized.
I thought it would be a good idea to focus an article on the latest trends in front-end development. Yes, we've all heard those rejoinders, like HTML is not a programming language, but let me assure you, it's definitely a specialized skill.
In this article, I'll go through some interesting new trends that have emerged in front-end development.
Semantic HTML
When HTML was first created, it was used to present some basic formatting. You could bold certain text, you could underline certain text, etc. Later, this activity became interactive with forms. Over time, these forms became more complicated and AJAX-based tools really unleashed the possibility of what could be possible in a browser. Then, paired with the additional complexity of security and the fact that accessibility has become extremely important and essential, our plain old HTML friend started clearly showing its age. As a result, HTML had to evolve.
These days, the vast majority of new applications are served through the browser. In fact, sit back and think about it: How many new products that have been launched in the last 10 years that you use on your laptop or desktop are not web applications?
Most of these websites have some common features. They all seem to have a header, paragraphs, navigation footers, etc. For the longest time, we've been trying to twist tags, like div and span, with some clever CSS to act like a header, paragraphs, navigation footers, etc. Semantic HTML changes that.
Semantic HTML is a way of writing HTML that focuses on the meaning of the content rather than just its presentation. It involves using HTML elements that describe the structure and purpose of the content, making it more readable, accessible, and maintainable. Semantic HTML uses elements that provide meaning to the structure of the web page.
Semantic HTML is a standard, as defined by the World Wide Web Consortium (W3C) and maintained by the WHATWG (Web Hypertext Application Technology Working Group). The HTML specification, which includes the definition of semantic HTML elements, is a standard document that outlines the syntax, structure, and semantics of HTML. This specification is maintained by the WHATWG and is widely adopted by web browsers and other HTML parsers.
The use of semantic HTML elements, such as <header>
, <nav>
, <main>
, <section>
, <article>
, <aside>
, <footer>
, and others, is mandated by the HTML specification. These elements provide a standardized way to define the structure and meaning of web content, making it easier for browsers, search engines, and other tools to understand and interpret the content.
Although there may be some flexibility in how semantic HTML elements are used, the specification provides clear guidelines on their usage and meaning.
Semantic HTML in practice means that instead of writing code like:
<div class="paragraph"></div>
You instead write:
<paragraph />
Although seemingly minor, this has huge positive ramifications.
Semantic HTML means improved accessibility. Semantic HTML helps screen readers and other assistive technologies understand the structure and content of the web page, making it easier for people with disabilities to navigate and understand the page.
Sematic HTML translates to better search engine optimization (SEO). Search engines can easily understand the content and structure of the web page, which can improve the page's ranking in search engine results.
Semantic HTML also means easier maintenance and updates. Semantic HTML makes it easy to modify and update the content and structure of the web page, as the HTML elements provide a clear and consistent structure.
Finally, semantic HTML translates into an improved user experience. Semantic HTML can help create a more consistent and intuitive user experience, as the structure and content of the web page are clearly defined.
Let's understand this with an example. Listing 1 shows a simple web page using non-semantic HTML. Let's read through it. The div
class nav tells you that this is navigation. But if this was a marine site, maybe it meant navy? What if it was a Native American site; it could mean Navajo. Okay, wait, the next section is a bunch of UL/LIs. From the time I learned HTML way back when, I learned that this is how you create an unordered list. So maybe this is navigation perhaps? Now I read the contents, home, about, contact, and I have a reasonable degree of confidence that this is navigation. And not naval navigation.
Listing 1: Non semantic HTML
<div class="nav">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="header">
<h1>Welcome to My Website</h1>
</div>
<div class="content">
<p>This is a sample web page.</p>
<p>Lorem ipsum dolor sit amet..</p>
</div>
But now let's look at the same code expressed in semantic HTML in Listing 2. It's pretty clear what it does because the nav tag, always means navigation. Not Navajo, naval, or navel. It's website navigation. You can imagine that in more complex pages, this is a huge help to accessibility, search engines, maintainability of code, and if everyone used these standards, it would create a much more consistent user experience.
Listing 2: Semantic HTML
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<header>
<h1>Welcome to My Website</h1>
</header>
<main>
<p>This is a sample web page.</p>
<p>Lorem ipsum dolor sit amet..</p>
</main>
The Dialog Tag
As a user interacts with your application, you frequently have to interrupt the flow to ask the user a question or present the user with some information. For example, when the user is attempting to do something sensitive, maybe you want to send them an SMS and have the user enter that SMS code. Alternatively, maybe the user is booking a ticket and you want to alert them that the ticket the user is attempting to buy has already been sold out.
Conventionally, we've shown this information via a pop-up window. The typical way of doing this is window.popup, as shown in Listing 3.
The code example in Listing 3 creates a button with the ID openPopupBtn
. Then it adds an event listener to the button that listens for a click event. When the button is clicked, you use window.open
to create a new pop-up window. You specify the URL of the pop-up content (popup-content.html), the name of the pop-up (myPopup), and the features of the pop-up (popupFeatures). You check to see if the pop-up was successfully opened by checking whether the window.open
method returned a non-null value.
Listing 3: Using window.popup
<button id="openPopupBtn">Open Popup</button>
<!-- JavaScript -->
<script>
const openPopupBtn = document.getElementById('openPopupBtn');
openPopupBtn.addEventListener('click', () => {
const popupWidth = 400;
const popupHeight = 300;
const popupFeatures = `width=${popupWidth},height=${popupHeight},left=100,top=100`;
const popup = window.open('popup-content.html', 'myPopup', popupFeatures);
if (popup) {
// Popup was successfully opened
console.log('Popup opened!');
} else {
// Popup was blocked by the browser
console.log('Popup was blocked!');
}
});
</script>
This sounds simple enough but it has some disadvantages, which the dialog tag intends to address. The <dialog>
tag was introduced in HTML5, which was published as a W3C Recommendation on October 28, 2014. However, the <dialog>
tag was actually added to the HTML5 specification in 2013.
The window.popup
or window.open
methods were initially created with good intent, to capture user information in a window, but now they're used frequently to open new tabs in background, frustrating the user with overlays and clickjacking, intending the user to get frustrated and click on things they don't intend to do, such as accepting cookies, increasing local storage quotas, or granting permissions, or any combination thereof. As a result, browsers have evolved to create pop-up blockers and enable them by default, which leads to poorer user experience, buggy applications, and more complicated code.
Secondly, the window.popup
window is modeless, i.e., the user is free to interact with the parent window while the pop-up is open. This again requires complex workarounds that are still not fool proof, and lead to poorer user experience.
The window.popup
also relies on a new HTML page, and has limited customizability when it comes to styling. You're effectively styling another HTML page, and browsers may choose to show the address bar, really marring your user experience.
Finally, window.popup
is not great for accessibility. It requires you to write complex code using manual focus management and ARIA attributes to ensure accessibility.
The dialog tag intends to solve all these issues and more. The same intent of Listing 3 can be expressed using a dialog tag, as shown in Listing 4.
Listing 4: Using the dialog tag
<!-- HTML -->
<dialog id="myDialog">
<h2>Dialog Title</h2>
<p>This is a sample dialog box.</p>
<button id="closeBtn">Close</button>
</dialog>
<button id="openDialogBtn">Open Dialog</button>
<!-- JavaScript -->
<script>
const openDialogBtn = document.getElementById('openDialogBtn');
const myDialog = document.getElementById('myDialog');
const closeBtn = document.getElementById('closeBtn');
openDialogBtn.addEventListener('click', () => {
myDialog.showModal();
});
closeBtn.addEventListener('click', () => {
myDialog.close();
});
</script>
There are a number of things that show up in Listing 4. At a high level, you create a <dialog>
element with an ID of myDialog
. You add a button with the ID openDialogBtn
to open the dialog. You then add a button with the ID closeBtn
to close the dialog. Then you use the showModal()
method to open the dialog as a modal window. And finally, you use the close()
method to close the dialog.
Compared to Listing 3, there are some clear advantages. This dialog blocks interaction with the rest of the page until it's closed. It's modal. This means that you don't have to implement complex workarounds because you can't make the dialog modal using window.popup. Think back to the ticketing application I was talking about a moment ago. If the user is attempting to book a ticket and the ticket is already sold out, you probably don't want the user to continue booking the ticket in the background and attempt to confuse your system. With the modal dialog, you achieve this very easily.
Additionally, the dialog automatically focuses on the first focusable element when it's opened, keeping the user in the flow of the application
. Also, the dialog automatically adds ARIA attributes to make it accessible to screen readers.
The only downside of the dialog tag is that the <dialog>
tag is not supported in older browsers, so you may need to add fallback code or use a polyfill for older browsers. At the time of writing this article, the dialog tag is supported in Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, and partially supported in Safari.
In case the dialog tag isn't supported by your intended browser, you can easily use dialog-polyfill available as an npm package, or via CDN. The code change is quite simple, as you can see Listing 5, effectively using the dialog shown in Listing 4, and using polyfill.
Improvement to HTML Forms
While we're on the topic of dialogs, let's also delve further into accepting inputs from the users that are done via the form tag. The HTML form tag typically has been the mainstay for accepting user input. Recent HTML5 improvements commonly available across modern browsers have made this task a lot easier. Let's dive into some of these new improvements.
The HTML5 form tag now includes a number of new attributes. These attributes are:
- A
placeholder
attribute to add a hint to form fields - A required attribute to specify whether a field is mandatory
- A pattern attribute that lets you define a regular expression for field validation
- The min and max attributes that allow you to specify the minimum and maximum values for number and date fields
- A step attribute that lets you define the increment/decrement step for number fields
Let's understand one of these. I pick the min and max attributes. Let's say that I wish to create an HTML form, and I want to ask the user a value for “quantity.” I also want to limit this to a minimum value of 1 and a maximum value of 10, with an initial value of 1. This can be achieved using the code snippet below:
<input type="number" id="quantity" name="quantity" min="1" max="10" value="1">
You need to consider older browsers that understand these attributes. This is a job for polyfills. You can find the full code in Listing 6. At a high level, you can assume that the HTML form includes a type=“number” input field with min and max attributes set to 1 and 10, respectively. Modernizr detects whether the browser supports HTML5 form features, including the min and max attributes. If the browser doesn't support these features, the jQuery polyfill kicks in. The polyfill listens for input events on the #quantity
field and checks the value against the min and max attributes. If the value is outside the allowed range, the polyfill adjusts the value to the nearest limit (either min or max).
Speaking of Modernizr, it's an incredibly useful library that you should know about. Modernizr is a JavaScript library that detects HTML5 and CSS3 features in a user's browser, allowing you to provide fallbacks or polyfills for older browsers that lack support for these features. It will detect support for features, and, based on the detection results, you can provide fallbacks or polyfills to ensure that your web application works across different browsers.
But I digress. Let's get back to HTML5 forms. Another great improvement is the introduction of new HTML5 form elements. Specifically, the datalist
element that provides a list of suggested values for a form field, the output element that displays the result of a calculation or user interaction, and the progress element that shows the progress of a task.
Let's look at one of these in action, although I encourage you to explore each of these. Listing 7 shows the output element in action. Listing 7 has a simple form with two number input fields (a and b) and an <output>
element (result). The oninput
attribute specifies a JavaScript expression to be executed when the user interacts with the form (e.g., changes the value of a or b). The JavaScript expression calculates the sum of the values of a and b and assigns it to the value
property of the result output element. The for attribute of the <output>
element specifies the IDs of the elements that contribute to the calculation (in this case, a and b).
To offer polyfill for this, you're effectively going to have the same logic again. I'm going to skip that because in today's world, every browser you probably care for supports the output tag. Let me leave you with an example of using the progress element. Also, try it out and see what it does.
<progress id="progressBar" value="50" max="100">
</progress>
The cool thing about the progress element is that it's fully scriptable via JavaScript. You just modify the .value
property of the element
. You can also highly stylize it. Listing 8 shows a fully stylized progress bar. I'm quite tempted to leave a screenshot of the progress bar here, but try it out.
Speaking of styling and forms, HTML5 also has some enhanced validation and feedback for forms. You could create field types like email and URL and you get automatic validation, but you can now style them with :valid
and :invalid css pseudo classes. Or you can use the setCustomValidity()
method to enable custom error messages for form fields.
Let's construct a simple example. First, create your user interface, as can be seen in Listing 9. This is a simple form that accepts a text box input and specifies its type to be email. Additionally, you're requiring this field by using the required attribute. I also have a div showing any error messages with the validation by the name of “emailError”.
Now let's style it. I'm going to keep it simple and specify two classes for valid and invalid. For the CSS gurus among you, you know you can add some very nice animations and effects here. But my code is rather simple. I simply change the border to green or red for valid or invalid, as can be seen below:
input:valid {
border: 2px solid #4CAF50;
}
input:invalid {
border: 2px solid #f44336;
}
This alone provides the user with some visual cues. But sometimes you want to have custom validation logic and show a custom error message. This is where our good old friend JavaScript comes in, and you leverage the setCustomValidity
method, as can be seen in Listing 10.
Now let's run this example. When you load the page
, it should show you an email field that is highlighted in red, because it's invalid. This can be seen in Figure 1.

As I start entering an email, it validates it for me and shows me an appropriate error message, as can be seen in Figure 2.

And finally, when I enter a proper email address, the field turns green, as per the CSS styling. This can be seen in Figure 3.

Vector Graphics
When HTML was invented, it had some very basic tags that allowed you to do some basic formatting like bold, italic, underline, etc. This was a while ago, when graphics were a luxury. As computers progressed, we were amazed to see a computer screen render an image. And it took forever to download it too. To support this, HTML introduced the IMG tag. As time went on, we came up with newer and better formats for representing images. The first format I remember was BMP, or bitmap for short. There may have been other formats, but at least this is the first format I remember. What I remember about it is that it was incredibly inefficient. The file sizes were huge. My jaw dropped when I saw a JPEG image for the first time. The file size was a fraction of what BMP was, but the quality was nearly the same
. Then came GIF, which could even do animations. GIF even allowed me to do transparent backgrounds. Then came PNG, which combined the best of both JPEG and GIF formats. Recently, Apple introduced the HEIC format which offers even better image quality and better compression than JPEG.
All of these formats had one shortcoming. These are all examples of raster graphics. Raster graphics, also known as bitmap graphics, are digital images composed of a grid of tiny squares called pixels. Each pixel has a specific color value, which, when combined, forms the final image. All of these formats are compression algorithms around raster graphics. Because they're pixel based, they're resolution dependent, and the final quality of the image depends on the screen and DPI being used to view an image. Also, the file sizes can get incredibly large as the images get larger. I use a 65" 8K screen as my monitor. Yes, I'm spoiled, but literally every image on the internet looks like crap.
SVGs, or scalable vector graphics, intend to solve this problem. SVG is an XML-based vector image format that allows the creation of dynamic, scalable, and interactive graphics. SVGs are made up of vectors, which are mathematical equations that draw shapes and lines rather than pixels. They can scale up or down without losing resolution. Additionally, their file sizes are tiny in comparison and they are editable in a text editor or via JavaScript.
Listing 11 shows a simple example using the SVG tag available in HTML5. This should produce an image like that shown in Figure 4.

What's unique about this image is that not only did you express it in plain text, but no matter how much you zoom in or out, this image doesn't lose its resolution.
This opens up some very interesting capabilities. Think about it. This SVG tag that you expressed was in plain text. Theoretically speaking, you could use plain JavaScript code to manipulate this image on the fly. Indeed, SVG supports that, and interesting concepts like animations, and all sorts of visual effects. There is a rather interesting library called D3.js that takes this to the ultimate lengths.
Let's look at a simple example of using D3.js
to render out a chart.
Typically, you're given data that looks like Listing 12, and the task given is: “turn this into a bar chart.” Historically speaking, I would have rendered this as an image on the server side and downloaded that PNG. Very clever! But with SVG, you can do this entirely client side. Here's how.
First, let's define the HTML
, as can be seen in Listing 13. The key parts here are that I'm referencing D3.js, I've defined a div on the page where my chart will go, and I'm externalizing all my logic in script.js.
Now let's focus on script.js. The first thing I'd like to do is give my chart some dimensions and add an SVG tag to my HTML page to hold my chart. This can be seen in Listing 14.
Let's set up the X axis, which can be seen in Listing 15. This code creates a band scale using D3.js, which is used to map the labels to a continuous range of values to the X axis. The .range([0,
width]) specifies the range of the scale, which is the width of the SVG element. You'll see that .domain(data.map(d ⇒ d.label)) specifies the domain of the scale, which is the list of labels from the data
, and .padding(0.2) adds a padding of 20% between each band, making the bars more spaced out.
Once this is set up, the svg.append
line creates a new group element (g) in the SVG and appends it to the SVG element. It then sets the transformation attribute of the group to translate it to the bottom of the SVG (by setting the Y-coordinate
to the height of the SVG). Then you call the d3.axisBottom(x)
function to create a bottom-oriented axis using the previously defined X scale. Finally, using the transform attribute, you rotate the text slightly so it looks nice in the final generated chart, which can be seen in Figure 5.

Now let's focus on the Y
axis. The Y-axis is slightly more interesting. You have a range of values to plot, so you probably want to scan the values and find the min and max and scale your axis accordingly. That is just rote math, so to keep my code simple, I'll just go with a max of 200 and hardcode it in my code. The full code for setting up the Y
axis can be seen in Listing 16.
Next, you need to plot the bars. The code to plot the bars can be seen in Listing 17. Let's dissect this code bit by bit.
The svg.selectAll
command selects all elements with the tag name “mybar” within the SVG element, of which, initially, there are none because you haven't plotted anything yet. It then binds the data array to these elements using the data()
method.
svg.selectAll("mybar")
.data(data)
You use the join method to create, update, or remove elements based on the bound data. For example, in the code, you're creating a new rect element based on the data array.
.join("rect")
Then, you set the label
, width (bandwidth), and fill color of the rectangle
, as shown below:
.attr("x", d => x(d.label))
.attr("width", x.bandwidth())
.attr("fill", "#69b3a2")
Finally, you set the initial height and y position of each rectangle.
.attr("height", d => height - y(0))
.attr("y", d => y(0))
But why are the initial positions zero?
It's because you're going to add animations. You want the bars to get plotted from left to right and grow from bottom to top. The code for that can be seen in Listing 18. The first three lines of this code basically describe that you wish to have a transition of duration 800 milliseconds. And the last three lines specify the values you wish to change in this transition.
All put together, the chart looks like Figure 6.
A picture doesn't do animations justice, so I put this code on a CodePen here: https://codepen.io/maliksahil/pen/gbOmQZd.
Summary
What I love the most about being a software developer is that the constant pace of change never ceases to amaze me. Imagine if I'd decided to become a doctor or a pilot. From the moment I started my career to now, the basic concepts would have remained the same
. As a doctor, I'll just tell my patients to exercise more, and if all else fails, blame it on age or insurance. As a pilot, it's basically the same controls, rules, flight controls, and concepts. Imagine what a pilot would feel like if they'd gone through the same transition as we did in the field of programming. Just 20 years ago, we used to use dial up internet, waiting patiently for an image to download, which could take 30 minutes. And boy did it suck when the phone rang in the middle of that download and all you could see was the top half. Then came JPG, and the whole image downloaded within a minute. I was simply blown away. Then we saw charts, rendered as images on the server
, and we could create impressive dashboards delivered via the web. And now, you have fancy animations running entirely client-side on your phone.
Mind blown, simply mind blown. This pace isn't going to slow down. And I'm so glad I picked this field of work. There's never a boring day.
Until next time, happy coding!
#### Listing 5: Using the dialog tag with polyfills for unsupported browsers
```html
<dialog id="myDialog">
<h2>Dialog Title</h2>
<p>This is a sample dialog box.</p>
<button id="closeBtn">Close</button>
</dialog>
<button id="openDialogBtn">Open Dialog</button>
<script>
// Check if the browser supports the <dialog> tag
if (!('showModal' in HTMLDialogElement.prototype)) {
// If not, load the polyfill
document.write('<script src="https://cdn.jsdelivr.net/npm/dialog-polyfill@0.4.9/dialog-polyfill.js"><\/script>');
}
// Wait for the polyfill to load
document.addEventListener('DOMContentLoaded', () => {
const openDialogBtn = document.getElementById('openDialogBtn');
const myDialog = document.getElementById('myDialog');
const closeBtn = document.getElementById('closeBtn');
openDialogBtn.addEventListener('click', () => {
myDialog.showModal();
});
closeBtn.addEventListener('click', () => {
myDialog.close();
});
});
</script>
Listing 6: Using min/max attributes via modernizr and jquery polyfills
// Modernizr detection
if (!Modernizr.inputtypes.number || !Modernizr.formvalidation) {
// jQuery polyfill
$(document).ready(function() {
$('#quantity').on('input', function() {
var min = parseInt($(this).attr('min'));
var max = parseInt($(this).attr('max'));
var val = parseInt($(this).val());
if (val < min) {
$(this).val(min);
} else if (val > max) {
$(this).val(max);
}
});
});
}
Listing 7: The output element
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
<input type="number" id="a" value="0">
<input type="number" id="b" value="0">
<output for="a b" id="result" name="result">0</output>
</form>
Listing 8: The progress bar styling customizability
progress {
width: 100%;
height: 20px;
border: none;
border-radius: 10px;
background-color: #f2f2f2;
}
progress::-webkit-progress-bar {
background-color: #f2f2f2;
}
progress::-webkit-progress-value {
background-color: #4CAF50;
}
progress::-moz-progress-bar {
background-color: #4CAF50;
}
Listing 9: Simple email input
<form id="emailForm">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
<div id="emailError"></div>
</form>
Listing 10: Using the setCustomValidity method
const emailInput = document.getElementById('email');
const emailError = document.getElementById('emailError');
emailInput.addEventListener('input', () => {
if (emailInput.validity.typeMismatch) {
emailInput.setCustomValidity('Please enter a valid email address.');
emailError.textContent = 'Please enter a valid email address.';
} else {
emailInput.setCustomValidity('');
emailError.textContent = '';
}
});
Listing 11: SVG Graphics
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#007bff" />
<rect x="20" y="20" width="60"
height="60" fill="#dc3545" />
</svg>
Listing 12: Some random data
const data = [
{ label: 'Jan', value: 100 },
{ label: 'Feb', value: 150 },
{ label: 'Mar', value: 200 },
{ label: 'Apr', value: 120 },
{ label: 'May', value: 180 },
{ label: 'Jun', value: 130 },
{ label: 'Jul', value: 80 },
{ label: 'Aug', value: 20 },
{ label: 'Sep', value: 170 }
];
Listing 13: My chart HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="mychart"></div>
<script src="script.js"></script>
</body>
</html>
Listing 14: Add my svg to the page
// set the dimensions and margins of the graph
const margin = {
top: 10,
right: 30,
bottom: 90,
left: 40
};
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
const svg = d3.select("#mychart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
Listing 15: Setup the X axis
const x = d3.scaleBand()
.range([0, width])
.domain(data.map(d => d.label))
.padding(0.2);
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
Listing 16: Setup the Y axis
const y = d3.scaleLinear()
.domain([0, 200])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
Listing 17: Plot the bars
svg.selectAll("mybar")
.data(data)
.join("rect")
.attr("x", d => x(d.label))
.attr("width", x.bandwidth())
.attr("fill", "#69b3a2")
.attr("height", d => height - y(0))
.attr("y", d => y(0));
Listing 18: Add some animation for fun
svg.selectAll("rect")
.transition()
.duration(800)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value))
.delay((d, i) => {
console.log(i);
return i * 100;
});