Page building

This section talks about the basics of building pages in Halfmoon, explaining the wrappers, different sections, shortcuts, custom scrollbars, and the placement of other components. If you are already familiar with the concepts, you can jump right into the starter template generator below.

Basic page #

The conventional way of building a basic page is by wrapping everything inside a page wrapper (.page-wrapper) which is placed inside the DOM's <body>. The main content of the page is placed inside a content wrapper (.content-wrapper), which is a child element of the page wrapper.

Page title

Card title

<body>
  <!-- Page wrapper -->
  <div class="page-wrapper">
    <!-- Content wrapper -->
    <div class="content-wrapper">
      ...
    </div>
  </div>
</body>

Page sections #

The following components can also be used inside the page wrapper (.page-wrapper):

  • Navbars (both default, ie, fixed to top, and fixed to bottom) — see docs page (opens in new tab)
  • Sidebar — see docs page (opens in new tab)
  • Sticky alerts (toasts) — see docs page (opens in new tab)

When using the above components, a few things must to be kept in mind. These things are discussed below.

Navbars #

When using the default navbar (.navbar), the page wrapper must be given the .with-navbar class. Similarly, when using the navbar fixed bottom (.navbar.navbar-fixed-bottom), the page wrapper must be given the .with-navbar-fixed-bottom class.

Without these classes, the navbars will not be displayed. Moreover, the navbars must be the immediate children of the page wrapper.

Sidebar #

When using the sidebar (.sidebar), the page wrapper must be given the .with-sidebar class. Without this class, the sidebar will not be displayed. Once again, the sidebar must be the immediate child of the page wrapper.

You can also change the sidebar type by adding the data-sidebar-type="{type}" attribute to the page wrapper. The {type} can be full-height, overlayed-all, overlayed-sm-and-down, or full-height overlayed-sm-and-down. The overlayed sidebars require an extra overlay element (.sidebar-overlay), and this is also placed inside the page wrapper. You can learn more about sidebar types in the docs page (opens in new tab).

Sticky alerts (toasts) #

For sticky alerts (toasts) to work, there must be a container with the class .sticky-alerts inside the page wrapper. This can be empty.

Complete page #

With all that said, given below is an example of a complete page that contains all of the components discussed above. Please make sure to click on the Toast! button to see how the toasts are positioned.

The above example is loaded in an iFrame, so a few things may look out of place depending on your browser settings and screen size. Open it in a new tab
<body>
  <!-- Page wrapper with navbar, sidebar, navbar fixed bottom, and sticky alerts (toasts) -->
  <div 
    class="page-wrapper with-navbar with-sidebar with-navbar-fixed-bottom" 
    data-sidebar-type="overlayed-sm-and-down">
    <!-- Sticky alerts (toasts), empty container -->
    <div class="sticky-alerts"></div>
    <!-- Navbar -->
    <nav class="navbar">
      ...
    </nav>
    <!-- Sidebar overlay -->
    <div class="sidebar-overlay" onclick="halfmoon.toggleSidebar()"></div>
    <!-- Sidebar -->
    <div class="sidebar">
      ...
    </div>
    <!-- Content wrapper -->
    <div class="content-wrapper">
      ...
    </div>
    <!-- Navbar fixed bottom -->
    <nav class="navbar navbar-fixed-bottom">
      ...
    </nav>
  </div>

  <!-- Requires halfmoon.js for toggling sidebar, and toasts -->
  <script src="path/to/halfmoon.js"></script>
</body>

Modals #

While this is not a hard requirement, modals should be defined before the page wrapper (.page-wrapper), so that whenever a modal is opened, the scrollbars inside the actual page (if any) are hidden. This way, if you plan to use scrollable modals on your page, the modal scrollbars will look a tiny bit better. See the code below to get a better idea.

Modals come before the page wrapper
<body>
  <!-- Modals -->
  <div class="modal" id="modal-1" tabindex="-1" role="dialog">
    ...
  </div>
  <div class="modal" id="modal-2" tabindex="-1" role="dialog">
    ...
  </div>

  <!-- Page wrapper -->
  <div class="page-wrapper">
    ...
  </div>
</body>

You can learn more about modals in their docs page (opens in new tab).

Shortcuts #

There are two built-in shortcuts which can be enabled by adding the following attributes to the DOM's <body>:

  • data-dm-shortcut-enabled="..." enables the shortcut for toggling dark mode by pressing shift + D.
  • data-sidebar-shortcut-enabled="..." enables the shortcut for toggling the sidebar by pressing shift + S.

Please note that these shortcuts will not interfere with input elements, as they fire only if no input element is focused. Moreover, they will also not interfere with browser shortcuts, as those are given priority. The following code shows a page with both the shortcuts enabled:

Attributes for shortcuts
<!-- Page with both the shortcuts enabled -->
<body data-dm-shortcut-enabled="true" data-sidebar-shortcut-enabled="true">
  ...
  <!-- Requires halfmoon.js -->
  <script src="path/to/halfmoon.js"></script>
</body>

Custom scrollbars #

If you are using this site on a modern browser, you may have noticed that the scrollbars have custom styles. Custom scrollbar styles are not applied by default, but instead have to be turned on by adding the following classes to the DOM's <body>:

  • .with-custom-webkit-scrollbars adds custom scrollbars on WebKit based browsers, such Google Chrome, Microsoft Edge (new), Safari, etc.
  • .with-custom-css-scrollbars adds custom scrollbars using the CSS Scrollbar properties . As of 2020, this is only supported on the modern Firefox browsers.

Using both the classes together is a pretty good way of ensuring that most of your users will have custom styled scrollbars, which change their appearance based on the mode (light or dark). The following code shows a page with custom scrollbars:

Classes for custom scrollbars
<!-- Page with custom scrollbars -->
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars">
  ...
</body>

The scrollbar styles are only applied when screen width > 768px. The devices that fall below this (tablets and mobile phones) come with disappearing scrollbars which are highly usable, and look good on both modes.

CSS scrollbar with transparent track Since v1.1.0+

When the .with-custom-css-scrollbars class is used to create custom scrollbars, the scrollbar-color: var(--thumb-color) var(--track-bg-color) property is used to style the scrollbars. If the --track-bg-color is set to transparent, then the scrollbar's track is given a transparent background, but the border is also removed. For this reason, the custom CSS scrollbars styled using the scrollbar-color CSS property are always given a solid track background color (not transparent), because having a border improves the usability of the scrollbars.

However, there is a utility class that can be used on containers to make sure that the custom CSS scrollbar has a transparent track: .css-scrollbar-transparent-track. This is useful for containers where the solid track background color does not look good, and the lack of border is an acceptable trade-off.

As mentioned above, this obviously only applies to browsers that support the CSS Scrollbar properties (as of 2020, this is only supported on the modern Firefox browsers). The custom scrollbars on Webkit based browsers are always given transparent tracks along with borders (because the browsers support it).

Set the preferred mode on load #

The user's saved mode preference (light mode or dark mode) can be automatically set on page load (ie, if the user prefers dark mode, then the dark mode will be set automatically) by adding the data-set-preferred-mode-onload="..." attribute to the DOM's body.

Please note, prior to v1.1.0, this attribute was actually called data-set-preferred-theme-onload. This alias still works for the sake of backward compatibility. However, it is strongly recommended that you use the new attribute name, and not the old one.

Anyway, the following code shows how the preferred mode can be set on page load using JavaScript:

Attribute for setting the preferred mode on load
<!-- Set preferred mode on load -->
<body data-set-preferred-mode-onload="true">
  ...
  <!-- Requires halfmoon.js -->
  <script src="path/to/halfmoon.js"></script>
</body>

How it works

If the preference cookie exists, this is given the first priority, because it means that the user has interacted with the page and set their own preferred mode (light or dark). If the cookie does not exist, then the priority is given to the user's system's color preference (prefers-color-scheme: dark).

Slight delay before setting the preferred mode

One possible issue you may have noticed using this method is the slight delay that occurs before the preferred mode is set. This is because the DOM has to be fully loaded before the JavaScript can actually attach a class to the DOM's body and set the preferred mode. This waiting causes the tiny delay. One way to fix this is by using a JS framework that can change routes without needing to reload the full page, ie, single page applications (SPAs).

Another way to mitigate this issue is with server-side rendering. In this case, simply avoid using the data-set-preferred-mode-onload="..." attribute, read the cookie server-side, and use it to set the correct mode when generating the template on your server. This is the method that is being used on this documentation website, which runs on Django (on the server). The same concept can be applied for other frameworks as well, such as Rails, Laravel, Flask, etc.

Auto-scaling # Since v1.1.0+

By default, Halfmoon uses rem units for almost everything. For this reason, the document element's font-size is set to 62.5%, ie, html { font-size: 62.5%; } This makes it so that 1rem = 10px, which leads to easier calculations.

For screen widths equal to or above 1600px, and again for 1920px, the document element's font-size is increased to 75% and 87.5% respectively. This is known as auto-scaling, and it makes pages built using Halfmoon look good on very large screens. This is because everything is made larger to take up the extra space, leading to a nicer user experience in most cases. You can try this out by opening this page on a bigger screen, or by using your browser's dev tools to simulate one.

However, if you don't want your sites to scale up on larger screens, this feature can be disabled by adding the class .auto-scaling-disabled to the <html> element, like so:

Disable auto-scaling
<!-- Page with auto-scaling disabled -->
<html class="auto-scaling-disabled">
  ...
</html>

The only time px is used is when a fixed size is always required, especially if that fixed size is 1px. For example, properties such as border widths, outline widths, and divider heights use px.

Starter template generator #

Now that you are familiar with the concepts of building pages, you can use the generator below to create a starter template for your project.

The generator takes your settings and adds the appropriate classes and defines the required containers and elements. For example, if you select the Use navbar option, the page wrapper is given the class .with-navbar, and an empty navbar (<nav class="navbar">) is defined inside the page.

Simply select what you want on your page, and click on the Generate button. We have already generated one for you using the default settings.

Uncheck this to use the flattened CSS file (no variables) that supports IE11
Learn more (opens in new tab)
Default (fixed top)
Can be used together with default navbar
Vertical navigation (fixed left)
Will be ignored if you choose not to use a sidebar
Empty container to hold all of the toasts
Toggle using shift + D
Toggle using shift + S, will be ignored if you choose not to use a sidebar
For Chrome, Edge (new), Safari, etc.
For Firefox (as of 2020)
If the user prefers dark mode, then the dark mode will be turned on automatically
Check this if you don't want your site to scale up when screen widths are equal to or above 1600px, and again for 1920px
Output starter template
<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Meta tags -->
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
  <meta name="viewport" content="width=device-width" />

  <!-- Favicon and title -->
  <link rel="icon" href="path/to/fav.png">
  <title>Starter template - Halfmoon</title>

  <!-- Halfmoon CSS -->
  <link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/css/halfmoon-variables.min.css" rel="stylesheet" />
  <!--
    Or,
    Use the following (no variables, supports IE11):
    <link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/css/halfmoon.min.css" rel="stylesheet" />
    Learn more: https://www.gethalfmoon.com/docs/customize/#notes-on-browser-compatibility
  -->
</head>
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars" data-dm-shortcut-enabled="true" data-sidebar-shortcut-enabled="true" data-set-preferred-mode-onload="true">
  <!-- Modals go here -->
  <!-- Reference: https://www.gethalfmoon.com/docs/modal -->

  <!-- Page wrapper start -->
  <div class="page-wrapper with-navbar with-sidebar">

    <!-- Sticky alerts (toasts), empty container -->
    <!-- Reference: https://www.gethalfmoon.com/docs/sticky-alerts-toasts -->
    <div class="sticky-alerts"></div>

    <!-- Navbar start -->
    <nav class="navbar">
      <!-- Reference: https://www.gethalfmoon.com/docs/navbar -->
    </nav>
    <!-- Navbar end -->

    <!-- Sidebar start -->
    <div class="sidebar">
      <!-- Reference: https://www.gethalfmoon.com/docs/sidebar -->
    </div>
    <!-- Sidebar end -->

    <!-- Content wrapper start -->
    <div class="content-wrapper">
      <!--
        Add your page's main content here
        Examples:
        1. https://www.gethalfmoon.com/docs/content-and-cards/#building-a-page
        2. https://www.gethalfmoon.com/docs/grid-system/#building-a-dashboard
      -->
    </div>
    <!-- Content wrapper end -->

  </div>
  <!-- Page wrapper end -->

  <!-- Halfmoon JS -->
  <script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/js/halfmoon.min.js"></script>
</body>
</html>

Up next: Containers