惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
GRAHAM CLULEY
S
Security @ Cisco Blogs
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
WordPress大学
WordPress大学
Project Zero
Project Zero
S
Schneier on Security
P
Proofpoint News Feed
小众软件
小众软件
P
Privacy International News Feed
美团技术团队
L
LangChain Blog
Know Your Adversary
Know Your Adversary
J
Java Code Geeks
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
Microsoft Security Blog
Microsoft Security Blog
Engineering at Meta
Engineering at Meta
I
InfoQ
量子位
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
Spread Privacy
Spread Privacy
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
U
Unit 42
P
Privacy & Cybersecurity Law Blog
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Blog of Author Tim Ferriss
Latest news
Latest news
K
Kaspersky official blog
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 热门话题
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
S
Securelist
AWS News Blog
AWS News Blog
F
Fortinet All Blogs
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
Scott Helme
Scott Helme
Help Net Security
Help Net Security
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Tenable Blog

Ahmad Shadeed

Better fluid sizing with round() Use Cases for Field Sizing The Basics of Anchor Positioning Item Flow CSS Relative Colors Balancing Text In CSS Should masonry be part of CSS grid? CSS display contents CSS Grid Areas CSS Cap Unit An Interactive Guide to CSS Container Queries CSS :has() Interactive Guide CSS Nesting UX in DevTools CSS Nesting Future CSS: State Container Queries Rebuilding a comment component with modern CSS Conditional CSS with :has and :nth-last-child CSS Text balancing with text-wrap:balance CSS Masking Do we need CSS flex-wrap detection? My CSS Wishlist Conditional CSS CSS Style Queries Inside the mind of a frontend developer: Article layout Inside the mind of a frontend developer: Hero section CSS container queries are finally here The CSS behind Figma First Look At The CSS object-view-box Property Learn CSS Subgrid CSS :has Parent Selector Aligning Content In Different Wrappers Flexbox Dynamic Line Separator Hello, CSS Cascade Layers Building UI Components With SVG and CSS A Deep CSS Dive Into Radial And Conic Gradients Defensive CSS Building Real-life Components: Facebook Messenger Conditional Border Radius In CSS CSS Container Query Units Less Absolute Positioning With Modern CSS Aligning a Button Label Vertically Comparing Design Mockups To Code Result Using HSL Colors In CSS Custom Scrollbars In CSS Let CSS Container Queries For Designers The State of CSS Cross-Browser Development Overflow Issues In CSS Inspect Element As A Way To Increase Your Curiosity Handling Text Over Images in CSS Digging Into CSS Logical Properties Clipping Scrollable Areas On The inline-start Side Understanding Clip Path in CSS The Art of Building Real-life Components Handling Short And Long Content In CSS CSS Scroll Snap A Deep Dive Into CSS Grid minmax() CSS Variables 101 Finding The Root Cause of a CSS Bug Learn CSS centering How to detect browser support for Flexbox Gap CSS Mistakes While On Autopilot Digging Into the Flex Property Understanding CSS Multiple Backgrounds Aligning Logo Images in CSS Grid for layout, Flexbox for components Colors in CSS Thinking About The In-between Design Cases min(), max(), and clamp() CSS Functions Image Techniques On The Web Everything About Auto in CSS Learn Box Alignment Let Learn CSS Positioning Intrinsic Sizing In CSS CSS Grid Template Areas In Action Hiding Elements On The Web Creating a Variable Color Font From Scratch Building a Football Ticket With CSS and SVG Blending Modes in CSS CSS Variables With Inline Styles Implementing Dark Mode For My Website Rebuilding Apple Music Header in HTML & CSS Accessible Checkbox Layout Flickering On Browser Resize Enhancing The Clickable Area Size Custom Underlines with SVG Part 2: The Process of Implementing A UI Design From Scratch Building An Old Nav Design CSS Flexbox: 5 Real World Use Cases I Used CSS Inline Flex For The First Time The Process of Implementing A UI Design From Scratch Common CSS Issues For Front-End Projects Handling Long and Unexpected Content in CSS How to Build Web Form Layouts With CSS Grid Grid Layout Ah-ha Moment Enhancing Our Components with CSS :empty Building Resizeable Components with Relative CSS Units CSS Writing Mode The Journey of Learning Front End Web Development on a Daily Basis
Part 3: The Process of Implementing A UI Design From Scratch
Ahmad Shadeed · 2019-05-11 · via Ahmad Shadeed

The Layout Maestro

I spent years teaching CSS layout on this blog. I put everything I know into The Layout Maestro course: 70+ lessons and 150+ interactive examples that teach you how to think CSS layouts, not just memorize syntax.

Get the course

Article Parts

{% include note.html content = “This is Part 3 of the article: The Process of Implementing A UI Design From Scratch. If you haven’t read the previous parts, it’s fine, but I recommend at least to skim them to get a general idea.” %}

Top Courses

Section Title

It sounds easy and straightforward, but it’s really tricky if at some point we decided to add a “View more” link on the far right, or to add a short description paragraph below the title.

Here are some possible scenarios for the section title:

As a result, the HTML should account for two columns in that case. It’s more future proof and can help us scale the design without changing the HTML structure.

<section class="section">
  <div class="wrapper">
    <header class="section-header">
      <div class="section-header__start">
        <h2>Top Courses</h2>
      </div>
      <div class="section-header__end">
        <a href="#">
          <span>View All</span>
          <svg width="18" height="13">
            <use xlink:href="#arrow-right"></use>
          </svg>
        </a>
      </div>
    </header>
    <ol>
      <!-- List of courses -->
    </ol>
  </div>
</section>

Notice that the classes names are section-header__start and section-header__end. I used start and end instead of left and right to account for right-to-left designs. In RTL, the start and end will be flipped, so adding section-header__right and section-header__left will be confusing.

I added the below baseline CSS for the section header:

.section-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

This could handle the start & end wrappers and will distribute them nicely. With that in hand, it’s easy to scale the section header if we decided to add more content on the start or end sides.

Course Component

There are multiple variations of it and the code need to account for all of them.

<ol>
  <li>
    <article class="course">
      <a href="#">
        <div class="course__thumb">
          <img src="img/course-thumb.jpg" alt="" />
        </div>
        <div class="course__details">
          <h2>
            The Process of Implementing a UI Design From Scratch
          </h2>
          <div>course progress</div>
          <p>Ahmad Shadeed</p>
        </div>
      </a>
    </article>
  </li>
  <li>...</li>
  <li>...</li>
  <li>...</li>
</ol>
  1. I added an ordered list to include the top courses inside it. Since the courses are ordered from the most to less viewed, it has to be an ordered list. This will result in a semantic and more accessible HTML.
  2. I wrapped the whole course in an <article> element since it’s suitable to use for that case.

This is the initial look for now:

If you notice, the course thumbnail is large and taking a lot of space. I will add a temporarily inline CSS for the <li>. This will act as a limit so I can focus on building the course component.

Some baseline CSS for the course component:

.course {
  background: #fff;
}

.course img {
  max-width: 100%;
}

.course__details {
  padding: 8px;
}

.course__details > * + * {
  margin-top: 0.5rem;
}

.course__title {
  margin: 0;
  font-size: 16px;
  font-weight: normal;
  color: #4e4e4e;
}

.course__author {
  color: #7b7b7b;
  font-size: 14px;
}

Maybe you’ve noticed that the progress item is placed below the title, while in the design it’s the first one. Why? Because it’s better to write HTML as per importance and not the visual order.

By using Flexbox order property, it’s possible to move the progress item visually above the title.

.course__details {
  /* Other styles */
  display: flex;
  flex-direction: column;
}

.course__progress {
  order: -1;
}

It works, but the spacing is not behaving as expected. Since I used course__details > * + *, it will add margin-top to an item that is followed by another item. In my case, the course progress item is followed by the author. When I moved it visually, the spacing is still there.

I got two solutions for that particular case:

  • Adding top and bottom margin for the course title. This can work, but it’s not a future proof solution. What if the progress was removed? There will be an unneeded space then.

  • Using CSS :not pseudo class.

.course__details > *:not(:last-child) {
  margin-bottom: 0.5rem;
}

This solution is better is it’s not tied to a specific element and will work no matter how many elements are there.

Progress Component

Ok, we’re done with placing it and adding the perfect spacing. Let’s dive into building the progress.

How to represent it? Well, let me write down all the possible scenarios in my head:

  1. Border Images
  2. CSS Gradients
  3. Using Inline SVG
  4. SVG as CSS Background
  5. CSS Masks and Pseudo Elements
  6. <meter> or <progress> elements.
  7. A list of <span>

1) Border Images.

I wondered about the possibility of using border images to accomplish the design. It didn’t work, as far as I know, there is no way to divide the gradient and make the progress items rounded

.course__progress {
  width: 70px;
  border-top: 5px dashed orange;
  border-image: linear-gradient(to right, #f6b73c, #4d9f0c) 1;
}

I wished that there is away to customize the gradient as well. I didn’t want to waste time on this, so I’m stepping away from it.

2) CSS Gradients.

It’s possible to use gradients and control the width, height of the progress bar items. Let’s try that!

.course__progress {
  width: 100px;
  height: 4px;
  background-image: linear-gradient(
    90deg,
    #000,
    #000 75%,
    transparent 75%,
    transparent 100%
  );
  background-size: 20px 100%;
}

It works! But the one downside that the design have rounded corners for the progress bar items, also coloring them won’t be straightforward.

3) Using Inline SVG

By taking the benefit of using stroke-dasharray and stroke-dashoffset, it’s possible to get something similar to the original design.

<div class="course__progress">
  <svg width="87px" height="5px" viewBox="0 0 87 1">
    <line
      class="st0"
      x1="0"
      y1="0.5"
      x2="87"
      y2="0.5"
      stroke="#F0C808"
      stroke-width="4px"
      stroke-dasharray="9"
      stroke-dashoffset="16"
      stroke-linecap="round"
    />
  </svg>
</div>

Notice that I added the attribute stroke-linecap, it will help in making the dashed items rounded.

However, if we want to color the first and second bar items, it’s not possible to color them in SVG unless we duplicate it in HTML which is a downside for me.

4) SVG as CSS Background

This is similar to the above option, I will use an SVG as a background image. Then, I will duplicate the same SVG with a different color to place it above the original one. Let’s see how it goes.

.course__progress {
  position: relative;
  width: 86px;
  height: 5px;
  order: -1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='87px' height='5px' viewBox='0 0 87 1'%3E%3Cline class='st0' x1='0' y1='0.5' x2='87' y2='0.5' stroke='%23000' stroke-width='4px' stroke-dasharray='9' stroke-dashoffset='16' stroke-linecap='round'/%3E%3C/svg%3E");
}

.course__progress:after {
  content: "";
  position: absolute;
  left: 0;
  top: -6px; /*It should be 0 but I made it -6px to demonstrate how it works*/
  width: 100%;
  height: 100%;
  background-image: url("data:image/svg+xml..."); /*Same as the parent background image but with a different stroke color.*/
}

I added a pseudo element to the progress <div> and now we can control the actual progress by increasing or decreasing the width value which can be controlled by Javascript if needed.

5) CSS Masks and Pseudo Elements

Another option is to use CSS Masks and pseudo elements. One of them will act as the base grey and the other is the colored one.

.course__progress {
  position: relative;
  /*Other styles*/

  &:before,
  &:after {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
  }

  /*The light grey progress*/
  &:before {
    content: "";
    background: #f2f2f2;
    mask-image: url("data:image/svg+xml...");
  }

  /*The orange progress*/
  &:after {
    content: "";
    width: 70%;
    background: #f0c808;
    mask-image: url("data:image/svg+xml..."); /*Same as the previous mask but with a different stroke color.*/
  }
}

What I like about this solution that it fallback really well. Here is how the progress will look in case mask-image is not supported.

6) <meter> or <progress> elements.

I rarely see someone talking about those two elements. I thought that they could be a good use case for the progress bar we have.

But, which one is the most appropriate? <meter> is good to be used for representing a value that is a scalar value within a known range. While the <progress> shows the completion progress of a task for example.

For our case, <meter> is more appropriate. Here is how it looks by default:

What if I customized the background of the bar and the meter background? I tried that by using CSS Gradients for the background, and end up with this:

meter {
  position: absolute;
  left: 0;
  top: 0;
  background-image: linear-gradient(
    90deg,
    #efefef,
    #efefef 75%,
    transparent 75%,
    transparent 100%
  );
  background-size: 20px 100%;
}

meter::-webkit-meter-bar {
  background-image: linear-gradient(
    90deg,
    #efefef,
    #efefef 75%,
    transparent 75%,
    transparent 100%
  );
  background-size: 20px 100%;
}

Note that the above is not the complete styling. I grabbed a reset CSS from this demo by Scott O’Hara and worked based on it. In the below video, I’m changing the value attribute of the <meter> and it’s dynamically updates the result.

7) A list of <span>

There will be 5 <span>s in the HTML. Each one will act as a progress bar item. I will add .active class to the ones that needs to be in orange.

<div class="course__progress">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
.course__progress span {
  display: inline-block;
  width: 13px;
  height: 5px;
  background: #f2f2f2;
  border-radius: 10px;
}

.course__progress span.active {
  background: #f0c808;
}

The moment of truth. I would go with the CSS Masks option as this one is the most appropriate and it fallbacks well. However, you might pick a different option depending on your project.

As a friendly reminder, don’t forget to make the progress accessible. I will add aria-label to account for that.

<div class="course__progress" aria-label="50% done"></div>

Some Tweaks For The Course Card Component

I added a box-shadow and border-radius for the course thumbnail and details elements.

.course img {
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
}

.course__details {
  background: #fff;
  border-bottom-left-radius: 3px;
  border-bottom-right-radius: 3px;
  box-shadow: 0 3px 7px 0 rgba(0, 0, 0, 0.08);
}

You might thought about why I didn’t add border-radius: 3px and overflow: hidden to the course wrapper itself instead of adding the radius for the course thumbnail and details elements? Well, that’s a good question.

The reason is that using overflow will prevent us from adding elements that needs to go outside the course wrapper. For example, we have a featured course with a ribbon that is placed at the top-left corner.

Course Thumbnail

I didn’t touched this yet. In short, it needs to be fluid and its height is dynamic and proportional to the width of the course component.

I’m using the aspect ratio technique which you can read about here in more details.

.course__thumb {
  position: relative;
  height: 0;
  padding-bottom: 50%;
}

.course__thumb img {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Having that, the image will act dynamically depending on the width of the course component. See below video:

Courses Grid

Now that the course component is initially ready, I can go further and work on the grid that will contain all courses.

The first thing I did is that I added the following CSS:

.l-grid-4 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 24px;
}

@media (min-width: 1180px) {
  .l-grid-4 {
    grid-gap: 30px;
  }
}
  1. Notice that I used the identifier l- for the class name l-grid-4. It stands for Layout and I named it like that so I can reuse it with other grids in the design. It’s borrowed from Smacss though I’m not using any CSS naming convention as it’s out of the scope of this article.
  2. I’ve used minmax function and told it to make the column width at least 200px and the maximum width is 1fr. See below video:

Equal Height Columns

I need to test and ensure that all course components height will be equal in case one of them has a long title. Sorry to say that it failed.

Well, CSS Grid has done it’s job. Notice that all the columns are aligned to the bottom and their height is equal. However, I need to do more work in order to achieve the result I want.

.course {
  height: 100%; /* [1] */
}

.course > a {
  height: 100%;
  display: flex;
  flex-direction: column; /* [2] */
}

.course__details {
  flex-grow: 1; /* [3] */
}
  1. The .course component take 100% height.
  2. The <a> takes 100% height and also changing it to a flex container.
  3. This will make the element .course__details fill the available space.

It’s working as expected. But in mobile, it will list course components one below the other and this could take a lot from the screen size. What to do in that case?

I’m thinking about the below possible solutions:

1) Make the Top Courses section as a slider

This is meant to make the section behave as a slider in mobile. What’s good about it?

  • It will save a good amount of space.
  • It’s suitable for this kind of content, where it’s fine to make it hidden.
  • Works as a great indicator for the user to scroll and play with it.
@media (max-width: 780px) {
  .top-courses {
    display: flex;
    grid-gap: 12px;
    overflow-y: auto;
    padding-bottom: 16px; /* To avoid cutting off the box-shadow of the corurse when adding overflow. */
    -webkit-overflow-scrolling: touch; /* Smooths the scrolling on iOS Safari */
  }

  .top-courses li {
    min-width: 200px;
  }

  .course__title {
    font-size: 14px;
  }
}

2) Make the thumbnail and course details on the same line.

What I like about that solution is:

  • It give us the ability to see almost all courses (Depending on your screen size) without scrolling down to see content.
  • The thumbnail size is small and not taking too much space.
@media (max-width: 450px) {
  .top-courses {
    grid-gap: 16px;
  }

  .course.course a {
    display: flex;
    flex-direction: row;
  }

  .course__details {
    border-bottom-left-radius: 0;
    border-top-right-radius: 3px;
  }

  .course__thumb {
    flex: 0 0 120px;
    height: 120px;
    padding-bottom: 0;
  }

  .course__thumb img {
    border-top-right-radius: 0;
    border-bottom-left-radius: 3px;
  }

  .course__title {
    font-size: 14px;
  }
}

3) To show two courses per row.

With CSS Grid at hand, it’s easy to change the minimum width of the column to allow for two columns per row.

For me, it looks good and we can even see more courses at without scrolling.

@media (max-width: 780px) {
  .top-courses {
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  }
}

What should I go for? I think the 3rd solution is the most appropriate for the case I have. That doesn’t mean that the rest of solutions are not good. Actually, they’re great. But it’s more of a personal preference for now.

Aligning The Inner Content Of The Course Card

In case one of the courses title was long, the author names are not aligned at the same line. In order to fix that, I will add margin-top: auto to the .course__details element (The one that contains the title, progress and author).

It works because the course details is a Flex wrapper using margin-top: auto will push the instructor name to the end.

A course component might be featured. To show that to the user, there should be a ribbon placed at the top left corner. Below is a close up of the ribbon. Notice the rounded corners!

I got a couple of ideas for doing it:

1) Clip Path

It’s a bit tricky in our case because the ribbon corners are all rounded except the top-right and bottom-left ones, they are clipped from the edges.

In order to accomplish this, I will need to shift the clipping path so it’s only clip half of the rounded corner. See below for an explanation:

First Option

.element {
  clip-path: polygon(0 0, 100% 0, 100% 0, 0% 100%);
}

Second Option

.element {
  clip-path: polygon(0 0, 100% 0, 104% 0, 0% 104%);
}

And here is the final result:

2) Clip Path + Pseudo Element

This concept is similar to the previous one, but without shifting the clip path. I added a pseudo element to act as a rectangle, then rotated and scaled it with CSS Transforms.

.course.is-featured {
  position: relative;
}

.course.is-featured:after {
  content: "";
  position: absolute;
  left: -6px;
  top: -6px;
  width: 100px;
  height: 100px;
  background: #ffb202;
  border-radius: 10px;
  clip-path: polygon(0 0, 100% 0, 100% 0, 0% 100%);
}

/* The rotated rectangle */
.course.is-featured:before {
  content: "";
  position: absolute;
  z-index: 1;
  top: 28px;
  left: 3px;
  width: 100px;
  height: 6px;
  border-top-right-radius: 5px;
  border-top-left-radius: 5px;
  background: #ffb202;
  transform: rotate(-45deg) scaleX(1.32) translateX(-12px);
}

Here is an explanation:

3) CSS Borders

I added a pseudo element and used the border hack to create a triangle. In brief, I made the right and bottom border colors transparent, which will hide them. After that, I added border-radius and it works like a charm!

.course.is-featured {
  position: relative;
}

.course.is-featured:after {
  content: "";
  position: absolute;
  left: -4px;
  top: -4px;
  border: 50px solid #ffb202;
  border-right-color: transparent;
  border-bottom-color: transparent;
  border-radius: 13px;
}

It’s easy, straight to the point and exactly I want.

I will choose the clip path option as it doesn’t feel as a hack for me. For the star icon, I will add it using the content property for the pseudo element.

The End

Hope you enjoyed it and thanks for reading! Do you have any feedback? Please ping me on Twitter @shadeed9

Codepen Demo

See the Pen Nadros - Part 3 by Ahmad Shadeed (@shadeed) on CodePen.