We use essential cookies for the website to function, as well as analytics cookies for analyzing and creating statistics of the website performance. To agree to the use of analytics cookies, click "Accept All". You can manage your preferences at any time by clicking "Cookie Settings" on the footer. More Information.

Only Essential Cookies
Accept All

Responsive Layout on Web

Overview

This topic describes how to adapt different Web components on multiple devices. It also introduces multi-device adaptation capabilities provided by the Web components, such as relative unit, media query, and window change listener, and provides adaptation solutions for the common layouts, such as grid, custom dialog box, and carousel layout.

Implementation Principles

Relative Unit

During web development, attributes such as the element size and margin are used to adjust the page effect. When defining these attributes, you need to use the following element size units provided by CSS:

  • Absolute unit: unit with a value that is fixed and not affected by any other element attributes, for example, px. This type of unit is applicable to elements with fixed sizes on pages.
  • Relative unit: unit with a value depending on other values or element attributes. When the value of the dependent element changes, the value defined by the relative unit changes accordingly. Due to the dynamic nature of the relative units, they are often used in responsive design of front-end pages. The following table lists common relative units.
Table 1 Common relative units
Expand

Unit

Relative Element

Application Scenarios

Sample Code

%

A percentage unit, relative to the size of the parent element.

It is often used in responsive design to adjust the size of an element relative to its parent element.

.parent {
  width: 400px;
}
.child {
  width: 50%; /* 200px */
}

em

Relative to the font size of the current element. If the font size of the current element is not set, it is relative to that of its parent element.

Used for font size and text-based spacing to change the layout.

p {
  font-size: 16px;
}
span {
  font-size: 1.5em; /* 24px */
}

rem

Relative to the font size of the root element (HTML element).

Similar to em, but rem allows for more consistent layouts because all rem values are based on the size of the same root element and are easy to adjust globally.

html {
  font-size: 16px;
}
p {
  font-size: 1rem; /* 16px */
}
span {
  font-size: 1.5rem; /* 24px */
}

vw/vh

vw is relative to the browser window width, and vh is relative to the browser window height.

The element size is entirely based on the window width, for example, a pop-up mask layer.

.overlay {
  width: 100vw; /* equal to the width of the viewport */
  height: 100vh; /* equal to the height of the viewport */
}
NOTE

The px unit set on CSS is automatically converted based on the Device Pixel Ratio (DPR), so that the px unit has the same visual effect as the vp unit on HarmonyOS. This feature shields the physical pixel differences of devices and facilitates the migration of web apps to HarmonyOS.

Media Query

Media query allows you to apply different style rules based on device features (such as screen size, resolution, and orientation). In this way, the web page can have a good display effect in different devices and screen sizes, thereby improving user experience. When the web page is adapted to multiple devices running HarmonyOS, the size ranges of horizontal and vertical breakpoints are the same as the recommended breakpoint ranges on HarmonyOS.

NOTE

Use the width-height ratio for the vertical breakpoints on the web side and the height-width ratio on HarmonyOS.

For example, in the following code for media query, when the viewport width is greater than or equal to 840 px, the size of the font in article style changes to 20 px. When the viewport width is greater than or equal to 320 px and less than 600 px, and the aspect ratio is greater than or equal to 1/1.2 and less than or equal to 1/0.8, it matches the criteria for a small window in horizontal split-screen mode on smartphones, and the font size will be changed to 14 px.

@media (840px<=width) {
  .article {
    font-size: 20px;
  }
}

@media (320px<=width<600px ) and (min-aspect-ratio: 1/1.2) and (max-aspect-ratio: 1/0.8){
  .article {
    font-size: 14px;
  }
}

Scenario 1: Changing the Font Size

Use the media query capability @media of CSS to set the font size in different breakpoints to implement responsive layout. The following uses sm, md, and lg as examples.

  • When the screen size is at the sm breakpoint, the size of the font in title style is 16 px.
  • When the screen size is at the md breakpoint, the size of the font in title style is 18 px.
  • When the screen size is at the lg breakpoint or above, the size of the font in title style is 20 px.
  • If the screen size does not meet the preceding requirements, the size of the font in title style is 14 px.
    .title {
      font-size: 14px;
    }
    @media (320px<=width<600px) {
      .title {
        font-size: 16px;
      }
    }
    @media (600px<=width<840px) {
      .title {
        font-size: 18px;
      }
    }
    @media (840px<=width) {
      .title {
        font-size: 20px;
      }
    }

As such, the font size automatically adjusts according to the screen size.

Scenario 2: Changing the Image Width

Use the media query capability @media of CSS to set the image width in different breakpoints to implement responsive layout. The following uses sm, md, and lg as examples.

  • When the screen size is at the sm breakpoint, the size of the element in cover style is 120 px.
  • When the screen size is at the md breakpoint, the size of the element in cover style is 160 px.
  • When the screen size is at the lg breakpoint, the size of the element in cover style is 240 px.
  • If the screen size does not meet the preceding requirements, the size of the element in cover style is 100 px.
    .cover {
      width: 100px;
      height: 100px;
    }
    @media (320px<=width<600px) {
      .cover {
        width: 120px;
        height: 120px;
      }
    }
    @media (600px<=width<840px) {
      .cover {
        width: 160px;
        height: 160px;
      }
    }
    @media (840px<=width) {
      .cover {
        width: 240px;
        height: 240px;
      }
    }

As such, the element size automatically adjusts according to the screen size.

Adding a Window Event

The window object provides the resize event listener. This event is triggered when the document view (window) is resized. If the relative unit and media query cannot be used to implement a responsive layout across devices, you can register a resize event listener in JavaScript, and then obtain the window height through window.innerHeight and the window width through window.innerWidth. Based on the obtained window size, you can use CSS and HTML to implement responsive design. The following example demonstrates how to proportionally adjust the font size based on the window width.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Responsive Font Size on Resize</title>
  <style>
    html {
        font-size: 16px;
    }
    .content {
        padding: 20px;
    }
    .content h1,
    .content p {
        margin: 0 0 1em;
    }
  </style>
</head>
<body>
<div class="content">
  <h1>Responsive Font Size Example</h1>
  <p>Resize the window to see the font size change.</p>
</div>
</body>
</html>
<script>
  const root = document.documentElement;
  const initialScale = window.innerWidth / 1920;
  root.style.fontSize = `${initialScale * 16}px`;
  // Listen for window size change events
  window.addEventListener('resize', () => {
      const newScale = window.innerWidth / 1920;
      root.style.fontSize = `${newScale * 16}px`;
  });
</script>

Layout Design

Grid Layout

CSS provides the grid layout, which is similar to the responsive grid layout (GridRow/GridCol). It divides web page content into grids and combines different grids to make various layouts.

Figure 1 Grid layout

This section describes only some key concepts of the grid layout. For more details, refer to the CSS Grid documentation.

  • Grid container and items: The grid layout consists of a parent element known as the grid container and its children, which are grid items.
  • Row and column: The grid container contains both the horizontal and vertical grid lines that divide the space into rows and columns.
  • Row spacing and column spacing: blank area between two rows or columns.

To use the grid layout, perform the following steps:

  1. Set the container attributes: Set the display attribute of the container to grid.
  2. Determine the arrangement mode of elements.
    1. Column width and row height: The column width is defined by grid-template-columns, in which the number of input parameters is the number of columns and the value of each parameter is the width of each column. The row height is defined by grid-template-rows in a similar way.
    2. Row and column spacing: The row spacing is defined by row-gap, and the column spacing is defined by column-gap. If the spacing between rows and columns is the same, use the gap attribute to control the spacing.

The following sample code demonstrates how to arrange grid items in two rows and three columns, with a column width and row height of 100 px and row and column spacing of 20 px.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo</title>
</head>
<style>
  .container {
    display: grid;
    gap: 20px;
    grid-template-columns: 100px 100px 100px;
    grid-template-rows: 100px 100px;
  }
  .container .grid-item {
    background-color: #f6fdf5;
    text-align: center;
    line-height: 100px;
  }
</style>
<body>
<div class="container">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
</div>
</body>
</html>
Figure 2 Final effect

When there is a small quantity of elements, you can define the column widths by writing the values one by one (for example, grid-template-columns: 100px 100px 100px). However, if there are a large quantity of elements, for example, 10 columns in a row, you can use repeat() to improve the code readability. This function has two parameters. The first parameter is the number of repetitions and the second parameter is the value to be repeated, for example, grid-template-columns: repeat(3, 100px). When multiple columns should be defined, repeat() significantly improves the readability and conciseness of the code.

The grid layout can be used with media query to implement responsive layouts across devices. You can arrange the layout with different size ranges to achieve different effects on various screen sizes. The following table lists the grid layout effects.

Table 2 Grid layout effect
Expand

Item

sm

md

lg

Final effect

  • At the sm breakpoint, the grid is arranged in four columns with a row spacing of 12 px.
    @media (320px<=width<600px) {
      .grid-functions {
        grid-template-columns: repeat(4, 48px);
        row-gap: 12px;
      }
    }
  • At the md breakpoint, the grid is arranged in six columns with a row spacing of 20 px.
    @media (600px<=width<840px) {
      .grid-functions {
        grid-template-columns: repeat(6, 48px);
        row-gap: 20px;
      }
    }
  • At the lg breakpoint, the grid is arranged in eight columns with a row spacing of 24 px.
    @media (840px<=width) {
      .grid-functions {
        grid-template-columns: repeat(8, 48px);
        row-gap: 24px;
      }
    }

Custom Dialog Box

Larger dialog boxes are displayed on large-sized devices to prevent the content from being too small to see. Use the media query capability @media of CSS to set the dialog box size at different breakpoints. The following table lists the dialog box effects.

Expand

Item

sm

md

lg

Final effect

  • At the sm breakpoint, set the dialog box size to 328 px × 344 px.
    @media (320px<=width<600px) {
      .custom-dialog {
        width: 328px;
        height: 344px;
      }
    }
  • At the md breakpoint, set the dialog box size to 360 px × 378 px.
    @media (600px<=width<800px) {
      .custom-dialog {
        width: 360px;
        height: 378px;
      }
    }
  • At the lg breakpoint, set the dialog box size to 393 px × 412 px.
    @media (800px<=width) {
      .custom-dialog {
        width: 393px;
        height: 412px;
      }
    }
NOTE

Responsive design is required not only for the size of the dialog box, but also for its content. However, because the content of the dialog box is highly customized, it is difficult to provide a unified manner for adaptation. You need to tailor an appropriate adaptation solution based on the content.

Carousel Layout

The carousel layout provides the function of playing multiple images in turn. Although the native Web does not provide the component that directly implements the carousel layout, you can use some methods or reuse the third-party component library to implement the carousel layout. The key points of responsive carousel layout design are as follows:

  • Control the size of the carousel element: Set the carousel image size at each breakpoint through media query, breakpoints, or window events.
  • Control the spacing between carousel elements: Determine the spacing based on the arrangement mode of carousel elements. When the flex layout is used, you can use the gap attribute to define the spacing. In other cases, you can use the margin attribute to control the spacing.
  • Control the distance of each move: Determine the distance based on the actual carousel implementation. If the carousel is performed by using translateX(), the step length increased each time needs to be controlled. If absolute positioning is used, the moving distance needs to be controlled by specified displacement attribute.

The following table lists the carousel effects and uses React as an example.

Table 3 Carousel layout effect
Expand

Item

sm

md

lg

Final effect

const Banner = () => {
  const banner = [
    { id: "001", url: "assets/banner01.png" },
    { id: "002", url: "assets/banner02.png" },
    { id: "003", url: "assets/banner03.png" },
    { id: "004", url: "assets/banner04.png" },
  ];

  const [currentIndex, setCurrentIndex] = useState(1);
  const [currentDot, setCurrentDot] = useState(0);
  const [width, setWidth] = useState<number>(0);
  const [singleOffset, setSingleOffset] = useState<number>(0);
  const [initOffset, setInitOffset] = useState<number>(0);
  const [gap, setGap] = useState(16);
  const [animate, setAnimate] = useState("transform 0.5s ease");
  const [dotVisible, setDotVisible] = useState(false);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const totalItems = banner.length;

  useEffect(() => {
    const updateLayout = () => {
      const winWidth = window.innerWidth;
      if (winWidth < 600) {
        setGap(0); // set the distance between elements under the sm breakpoint.
        setWidth(winWidth - 32); // set the element width under the sm breakpoint.
        setSingleOffset(winWidth - 32); // set the single displacement under the sm breakpoint.
        setInitOffset(0); // set the initial offset under the sm breakpoint.
        setDotVisible(true);
      } else if (winWidth < 840) {
        setGap(12); // set the distance between elements under the md breakpoint.
        setWidth((winWidth - 48 - gap) / 2); // set the element width under the md breakpoint.
        setSingleOffset(width + gap); // set the single displacement under the md breakpoint.
        setInitOffset(24); // set the initial offset under the md breakpoint.
        setDotVisible(false);
      } else {
        setGap(16); // set the distance between elements under the lg breakpoint.
        setWidth((winWidth - 250 - gap) / 2); // set the element width under the lg breakpoint.
        setSingleOffset(width + gap); // set the single displacement under the lg breakpoint.
        setInitOffset(125); // set the initial offset under the lg breakpoint.
        setDotVisible(false);
      }
    };

    updateLayout();
    window.addEventListener("resize", updateLayout);
    return () => window.removeEventListener("resize", updateLayout);
  }, [gap, width]);

  useEffect(() => {
    const interval = setInterval(() => {
      setCurrentIndex((prev) => prev + 1);
      setCurrentDot((p) => (p + 1) % banner.length);
    }, 3000);
    return () => clearInterval(interval);
  });

  useEffect(() => {
    if (currentIndex === totalItems + 1) {
      setTimeout(() => {
        setAnimate("none");
        setCurrentIndex(1);
        setTimeout(() => {
          setAnimate("transform 0.5s ease");
        }, 50);
      }, 550);
    }
  }, [currentIndex, totalItems]);

  return (
    <div className="banner-container">
      <div
        className="banner-wrapper"
        ref={wrapperRef}
        style={{
          transform: `translateX(-${
            currentIndex * singleOffset - initOffset
          }px)`,
          transition: animate,
          gap: `${gap}px`,
        }}
      >
        {[banner[banner.length - 1], ...banner, ...banner].map(
          (item, index) => (
            <div
              style={{
                width,
              }}
              key={`${item.id}-${index}`}
              className="banner-item"
            >
              <img src={item.url} alt={`banner-${item.id}`} />
            </div>
          )
        )}
      </div>
      {dotVisible ? (
        <div className="swiper-dot">
          {banner.map((item, index) => (
            <div
              key={item.id}
              className={`dot${currentDot === index ? " dot-active" : ""}`}
            ></div>
          ))}
        </div>
      ) : (
        <></>
      )}
    </div>
  );
};

export default Banner;
Search in Best Practices
Enter a keyword.