DEV Community

Elian Ibaj
Elian Ibaj

Posted on

Modern Web Dev - UI - HTML & CSS

As I was wrapping my head around everything I had to review about building web user interfaces in 2021, I came across this "I Design, You Build" challenge here on DEV:

Joy sparked. Challenge accepted! With a twist — I'll use this same design to build the website multiple times with all the different technologies that I want to explore:

  • HTML & CSS
  • CSS-in-JS (probably react & styled-components)
  • Tailwind CSS
  • Component library (probably Chakra UI)

In the intro to this blog series, I imagined I would only write one article about UI comparing all these technologies. But after finishing just the HTML & CSS part, it's obvious they each need their own blog. I will preface them all with "UI -" like I've done for this first one.

HTML & CSS like it's 2009

Except no. We now have very good browser support for Flex and Grid layouts — it would be a shame not to use them. Wait how do you use them for building full responsive pages again? Is mobile-first still the thing to do by the way? To soothe the sudden existential dread caused by the influx of these questions, I read this from start to finish:

https://www.smashingmagazine.com/2018/02/media-queries-responsive-design-2018/

It works. I mean, the best thing would've been a recording of this talk by the author, but such a recording doesn't seem to exist on the internet yet, so the article will have to do.

Okay so:

  • flexbox and grid are at the core of modern responsive web
  • mobile-first still makes sense but breakpoints should be set where the design breaks visually instead of actual device widths

Here's a video I watched to review what I know about CSS Grid:

Everyone's learning style is different, but for me, I remember I put that video at 1.5x playback speed, and it captured my attention long enough to watch to the end. After that, when you run into specific problems while building something with grid (as you will), gridbyexample.com will have your answers.

As for flexbox, I don't remember all the different places I've learned it from, but if this doesn't make sense to you, it probably means you should review one of the many good resources on flexbox out there:

While we're on the prerequisites part of this blog, do you know what else is required reading? Accessibility. Yeah, I know you plan on learning about accessibility... someday. And I can't blame you entirely, to be honest — putting the responsibility of making web browsers work correctly with assistive technology entirely on developers, feels akin to fighting climate change with paper straws. Still, there is a grain of truth in both of those outwardly absurd situations: just like the fight against climate change will most probably require some changes in consumer behavior, accessibility in its current form (WAI-ARIA, etc.) is necessary until we have better solutions to the problem of inaccessible technology.

Where do you start? This awesome introduction, right here:

And follow that with this practical one:

And here's the last piece of reading I'll suggest before we start working on the challenge. This is the most fun to read, though — it's an interactive guide about the basics of CSS animations:

https://www.joshwcomeau.com/animation/css-transitions/

Alright, so with our refreshed knowledge of responsive web design with flex and grid, accessibility, and animations, we are ready to jump right in and:

Start Challenge

Here's a first look at the design:
Screenshot of website's desktop and mobile designs

I've highlighted three important components in the desktop design: the top navigation bar, the products listing component which is a grid of items you can filter, and the showcase/slider/carousel.

We'll cover UI component libraries later in this series, but we must take a first look at them now as case studies to steal code copy best practices from. UI components, like the three that we have in our design, are very common patterns, that are implemented again and again in all kinds of websites and apps. It makes sense to get a bit more familiar with how others are doing that.

Ten years ago, that would have meant reading what some distinguished designer had to say about it in his (yes, the distinguished designer was a he) personal blog. Today, a good place to start would be the most popular design systems/component libraries. Of course, you should still read widely, listen to podcasts, and consume all kinds of content you like, but as a culmination (usually — not looking to start any debates here) of current best practices, popular component libraries are a good place to start.

Material Design is the best Google has to say about the subject. Chakra UI is a truly collaborative project built under the watchful eyes of the open-source community. Bootstrap suffers from the plague of having once been uber-popular, but is, in its current fifth version, an invaluable mix of best practices and real-world experience. We'll look at these libraries, and many other sources, to learn about the three components we identified.

Base Styles

I've separated each component into its own git branch, so that you can inspect them in isolation, without getting distracted by unrelated code. The master branch contains all of them put together.

Code

If the others are components one, two, and three, this is like component zero? While writing the base styles for the page, and the two simple sections that I didn't circle in red, we also have to take some things into consideration as they come up, that we haven't covered in the prerequisites section above. It's very easy to fall into deep rabbit holes through each of these topics, though, so I'll keep it super short and give no "further reading" links at all.

CSS Units and (Responsive) Typography

  • ems everywhere you'd use pixels before (except for stuff like border widths, or when you explicitly don't want something to change). They work well with a custom font size in the browser settings; make it easier to work with padding and generally let you worry only about the font-size of elements and have spacing adapt to it.
  • Manually set font-sizes at different breakpoints to what looks good at that size. We'll have three main breakpoints: small - under 48em, medium - up to 80em, and large - 80em and above. "Medium" and "large" will basically share the same desktop design but with font sizes and spacing adjusted.

Responsive Images

Doing art direction and handling different resolutions by hand, for a large website with dynamic content, is practically impossible. You will definitely need to reach for some images service like Cloudinary.

In our proof of concept here, I exported two sizes of big images like the hero image and used srcset and sizes to serve the most appropriate one. I didn't export a separate file for the cropped mobile version but achieved the crop effect with object-fit and object-position.

Other

  • Pixel-perfect-ish. I haven't gone through extra effort to make it pixel-perfect, but for the most part, except for where the design allows more flexibility, sizes and spaces are the same as design.
  • Modern browsers only. And even then, since this is pure HTML and CSS with no build step at all, I haven't even added browser prefixes that would normally be added by a tool like autoprefixer.

Main Navigation Bar

Probably the most ubiquitous piece of UI in websites. It has always been there in almost all websites, evolving with the time from a dl with links in the first website ever, to uls with padded lis floated left or right for the horizontal look that started in the 90s and continues on today, to the web 2.0 fad of dropdowns on hover, to the proliferation of hamburger menus despite people making the case against them.

From the popular component libraries mentioned above, Material Design's top app bar, even though heavily biased towards apps rather than websites, is still a good high-level overview. The first related link from that page is also a good read to get you to think more in terms of UI/UX rather than solely converting a design into a web page. In terms of implementation, bootstrap's Navbar seems closer to what we're looking for.

Speaking of which, what are we actually looking for in our nav component? We have both desktop and mobile designs so that's good:

Desktop and mobile design of navigation component

Now, I didn't make you read all those articles for nothing, so let's apply our knowledge to fully specify our nav component:

  • A full-screen off-canvas element that slides in when the menu toggle icon is pressed, seems like the most appropriate solution for the mobile version. It's elegant to match the design. And it's big enough to accommodate all the links to the full catalog that would normally be present in a furniture shop's website like this one.
  • The search and cart buttons are important enough to not be tucked away behind the hamburger menu, so we can leave them in the same place for mobile too, and move the toggle icon to the left.

Code

Observations:

  • I think being able to just do display: block; (the default) on mobile, and display: flex; on desktop to get a responsive menu is beautifully simple. Also, I didn't do ul > li for the menu items, and that's fine. You could do it, and that would be fine too.
  • You'll notice I haven't gone overboard with aria properties. Just some roles and labels for important icon buttons like search etc.
  • What I did pay attention to, with regards to accessibility, was making sure I don't break stuff that worked before, like navigating the page with the keyboard. And that's exactly what you do when you translate the menu off the screen. If you press tab now to move from the logo to the search button, the focus seems to disappear for a while as it's moving through the menu items (which are not visible on the screen). That's what visibility: hidden is for (as well as for hiding it from screen readers too). For more complicated components, you might have to manage the tabindexes of your elements with JavaScript to fix this, but in this case, using the CSS visibility property, and making sure it works well with our animation, was all that was needed.
  • I stole the hamburger icon animation from this beautiful solution of the challenge. I must've been procrastinating on something else, though, because instead of copying the SVG animation like a normal human being, I had to go and rewrite it in CSS.
  • I never knew I needed custom transition timing functions in my life but here we are. The cubic-bezier you see at .search's transition property is the "easeOutQuart" from here. Its inverse, the "easeInQuart", is used to complete the effect of making the search slowly show up (after the backdrop blur has started kicking in) and quickly get out of the way once you dismiss it.

Showcase / Slider / Carousel

Ah, the carousel — web designers' most hated component. This is probably why you won't find it in most of the popular component libraries (okay bootstrap still has one, but that's probably part of why bootstrap is not so hip anymore). The carousel is not going away anytime soon though. Especially on mobile where swiping is second nature. Airbnb's scrolling features on their home page and app are iconic by now. Swiping on, how do we implement it?

You may not find a carousel component in component libraries, but do you know where you can find one? At the WAI-ARIA Authoring Practices.

I took an even simpler approach, though. Here's the code first:

Code

As I said, I took a simpler approach. Not because I'm a contrarian, but because I found out about CSS Scroll Snap! Oh em gee! Is this real? How can this be real?

You add two CSS properties, and things... just... work? This just doesn't happen on the web. Who allowed this? Didn't they think of the slippery slope?

OK, for real, this is awesome, but let's move on.

With scroll snap, our carousel doesn't have to be a carousel at all. All the "slides" content will be present at all times, just waiting to be scrolled into view. As far as screen readers are concerned, this is normal content on the page (I gave slides a role="group", but even without it, screen readers would do fine reading the content in order).

For keyboard navigation, when the showcase has focus, pressing left or right arrow keys would normally just scroll the content horizontally by a little. Thanks to snap scroll, that little scroll becomes a complete scroll to the previous/next slide. scroll-behavior: smooth; ensures the same happens when you tab through, moving the focus from one slide to another (except for firefox apparently, but that's easy to fix with some JavaScript if you want).

The JavaScript for the buttons becomes trivial too: next/previousElement.scrollIntoView(). The IntersectionObserver keeps track of the current slide to conditionally disable the prev and next buttons as needed.


(Filterable) Products Listing Component

Have you seen that "drawing spiderman in 10 min, 1 min, 10 seconds challenge" that went viral a while ago?

The 10-second version of our page would just be the products listing component. It's the main function of the site. The reason why visitors are there. I learned to recognize this most basic pattern as soon as I got into web development. I started with WordPress — you had this index.php template to design the page that lists all the blogs/items, and single.php to design the individual view that shows up when the user clicks one of those items. That pattern is everywhere in different variations: youtube, twitter, news sites, your portfolio. See a bunch of things, click one.

Now, because this is so often the main functionality — it is closely tied to the business logic of the website/app — you won't usually find a generic "product listing" component in the component libraries. Instead, you'll find helpful bits intended to aid you while you're building this core feature yourself. For example, you'll find cards in material design, and this nice link overlay in chakra. Bootstrap also has cards, list groups, pagination etc.

Pagination is interesting because it's one of those "variations" that I mentioned above — a property of the items listing component. Filtering is, in this way, also a property of the component. An important property for sure, since it changes fundamentally the way users interact with the component, but it's still just a property, something that the component "has". I guess what I'm trying to say is don't think of this component as a filtering component first, but as a products listing component, with filtering. This will set the framework for thinking on how to implement it. Let's do that now.

  • The desktop layout screams CSS Grid. Three columns, and as many rows as needed. The mobile layout is horizontally scrollable. I made this snappy-scrollable (word?) as well, but it is a UX question to think about. The keyword here is reducing inertia. Is it easier for the user to scroll through the products with or without scroll snap?
  • The classic masonry is the first effect that comes to mind for animating the filtering. It animates both the items that are appearing/disappearing individually, as well as the movement of the surrounding elements to perfectly illustrate the filtering that is taking place. But I don't think it's a good fit in our case for a few reasons:
    1. In this proof of concept we can have all the products in the HTML at all times, and hide those that must be filtered out with CSS. But in a real-world implementation of this e-commerce website, you would be fetching the products from some backend. All that movement after a loading animation would make for a disjointed experience.
    2. It seems like animating the position of items would be rather complicated with CSS Grid. Because I made my decision after thinking of the first reason above, I haven't investigated this further, but I'm including it here as something to think about. This is not about being lazy, but about being mindful of budget and time restrictions. As mentioned above, this is the core feature of our website, so if you were to decide that having a masonry style effect was important enough, it would make sense to plan for and implement a solution for it. That solution might be anything, from researching libraries you could use that animate CSS grid, to switching to absolute position altogether. The point that I'm trying to make is... actually two points: 1) decide what is important and how much time you're going to spend on it, and 2) when you see something implemented differently from how you would do it, consider they most probably had a reason to do it the way they did — if appropriate, ask them why.

Code

Notes:

  • For animation, I ended up fading the elements in and out. Seems unobtrusive enough, and in line with the design and the rest of the animations. The js code might seem a bit complex, but it has to be that way because of the classic problem where you can't animate from display: block; to display: none;.
    • To make it work with loading, I would only fade the contents inside of .product, and infinitely animate the background color to indicate loading. I might do this in one of the upcoming blogs.
  • Using a radio group for our filter buttons solves most of the accessibility problems we would otherwise encounter. Keyboard navigation? Check. Focus on labels with :focus-visible? Check. The product list itself is a ul, and aria-live="polite" announces to screen readers when its content changes.

And without further ado (lol as if I could drag this out any longer), here's the full challenge demo and code:

Conclusion

I'll try to do this in a bulleted list form too. Here are my observations from building a website with HTML and CSS in late 2021:

  • EVERGREEN BROWSERS BABY!!! You have no idea how bad this was before. In immigration law terms, I believe our jobs qualify as "highly skilled labor". Well, for a long time, 99% of the high skill a web developer had was knowing about and resolving browser inconsistencies. I don't know how to tie a tie, but the information that Internet Explorer 6's XMLHttpRequest was not a native JavaScript object but an ActiveX one, is still taking up space somewhere in my brain.
  • This is still very much a full-time job. We only scratched the surface of accessibility here and didn't even get into performance. Anyone who speaks of full-stack has either stumbled upon — and is underpaying — the rare 10x coding creature or, much more probably, is neglecting many of the specialized areas just mentioned. (I have nothing against full-stack roles where responsibilities shift from time to time; this was just for the specific situations I mentioned here.)
  • The kids are alright. Not looking to rehash all the debates on tooling and over-engineering from the past few years (almost a decade now, really). Just making my observation, that, even when building a simple website in 2021, the "just opening up notepad and coding" ideal feels very self-limiting without getting much benefit in return.

Alright then, on the next blog we'll rebuild this in React and some CSS-in-JS library like styled-components. Follow me on twitter. I almost never tweet, but if someone follows me from here, I'll make an exception and tweet about the next article when it comes up.

Discussion (0)