Customize the loading icon

Customize the loading icon

Note that it changes the main loading icon, but not the small loading icon (composer, search menu, etc).

  1. Edit your current theme or create a new theme component.

  2. Go in </head> and paste this code:

    <script type="text/x-handlebars" data-template-name="components/conditional-loading-spinner">
    {{#if condition}}
    <div class="custom-loader-container">
    
      <div class="my-custom-loader">
      </div>
    
    </div>
    {{else}}
      {{yield}}
    {{/if}}
    </script>
    

    Replace or fill .my-custom-loader with your loader HTML code.

  3. Go in CSS and paste this code:

    .custom-loader-container {
        position: relative;
        margin: 20px 0 20px;
        display: flex;
        justify-content: center;
    }
    

    Add your custom loader CSS.

Examples

Pulsing circles

image

Preview: https://theme-creator.discourse.org/theme/canapin/loader-pulse

Original code from https://codepen.io/victordarras/pen/vHitB

</head>:

<script type="text/x-handlebars" data-template-name="components/conditional-loading-spinner">
{{#if condition}}
<div class="custom-loader-container">

  <div class="loader-pulser">
  </div>
  
</div>
{{else}}
  {{yield}}
{{/if}}
</script>

CSS:

.custom-loader-container {
    position: relative;
    margin: 20px 0 20px;
    display: flex;
    justify-content: center;
}
.loader-pulser {
  display: inline-block;
  vertical-align: middle;
  position: relative;
  margin-right: 3em;
  font-size: 10px;
  width: 3.2em;
  height: 3.2em;
  right: 2em;
  position: relative;
  &:before, &:after {
    content: '';
    border: 2px solid #ccc;
    border-radius: 50%;
    height: 100%;
    width: 100%;
    position: absolute;
    animation: pulse .5s ease-out;
    animation-iteration-count: infinite;
    opacity: 0;
  }
  &:before {
    border: 2px solid #aaa;
    animation-delay: .15s;
  }
}
@keyframes pulse {
  0% {
    transform: scale(0.1,.1);
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    transform: scale(1.2,1.2);
    opacity: 0;
  }
}

Fading bars (svg animation)

image

Preview: https://theme-creator.discourse.org/theme/canapin/loader-bars

Original code from https://codepen.io/aurer/pen/jEGbA

</head>:

<script type="text/x-handlebars" data-template-name="components/conditional-loading-spinner">
{{#if condition}}
<div class="custom-loader-container">
    <div class="bars-loader">
      <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="30px" viewBox="0 0 24 30" style="enable-background:new 0 0 50 50;" xml:space="preserve">
        <rect x="0" y="10" width="4" height="10" fill="#333" opacity="0.2">
          <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0s" dur="0.4s" repeatCount="indefinite"></animate>
        </rect>
        <rect x="8" y="10" width="4" height="10" fill="#333" opacity="0.2">
          <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.15s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.15s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.15s" dur="0.4s" repeatCount="indefinite"></animate>
        </rect>
        <rect x="16" y="10" width="4" height="10" fill="#333" opacity="0.2">
          <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.3s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.3s" dur="0.4s" repeatCount="indefinite"></animate>
          <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.3s" dur="0.4s" repeatCount="indefinite"></animate>
        </rect>
      </svg>
    </div>
</div>
{{else}}
  {{yield}}
{{/if}}
</script>

CSS:

.custom-loader-container {
    position: relative;
    margin: 20px 0 20px;
    display: flex;
    justify-content: center;
}
.bars-loader {
    svg path,
    svg rect{
      fill: #333;
    }
}

Unicycle

image

Preview: https://theme-creator.discourse.org/theme/canapin/loader-unicycle

Original code from https://codepen.io/Canapin/pen/gOrddVR

</head>:

<script type="text/x-handlebars" data-template-name="components/conditional-loading-spinner">
{{#if condition}}
<div class="custom-loader-container">

    <div class="unicycle-loader">
        <div class="saddle"></div>
        <div class="frame">
            <div class="hub"></div>
        </div>
        <div class="wheel">
            <div class="crank left">
                <div class="pedal"></div>
            </div>
            <div class="spokes">
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
                <div class="spoke"></div>
            </div>
            <div class="crank">
                <div class="pedal"></div>
            </div>
        </div>
    </div>
    
</div>
{{else}}
  {{yield}}
{{/if}}
</script>

CSS:

.custom-loader-container {
    position: relative;
    margin: 20px 0 20px;
    display: flex;
    justify-content: center;
}
$color: black;
$speed: 1.5s;
.unicycle-loader {
    margin: 20px auto 20px auto;
    position: relative;
    transform: rotate(7.5deg) scale(0.70);
    opacity: .2;
    .saddle {
      width: 50px;
      height: 15px;
      background-color: $color;
      margin-left: 30px;
      position: relative;
      border-bottom-left-radius: 10px;
      border-bottom-right-radius: 20px;
      transform: rotate(-5deg);
      &:before {
        content: "";
        display: block;
        width: 30px;
        height: 20px;
        background-color: $color;
        border-radius: 50%;
        position: absolute;
        left: -5px;
        top: -5px;
      }
      &:after {
        content: "";
        display: block;
        width: 20px;
        height: 10px;
        background-color: $color;
        border-radius: 50%;
        position: absolute;
        right: -7px;
        top: 1px;
        transform: rotate(-35deg);
      }
    }
    
    .frame {
      width: 7px;
      height: 45px;
      background: $color;
      margin-left: 51px;
      position: relative;
      z-index: 1;
      margin-bottom: 15px;
      &:before {
        content: "";
        height: 75px;
        width: 11px;
        background: $color;
        display: block;
        position: relative;
        top: 100%;
        margin-left: -2px;
      }
    }
    
    .hub {
      position: absolute;
      bottom: calc(-100% - 30px);
      left: calc(50% - 0.5px);
      transform: translateX(-50%);
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background-color: $color;
      border: 3px solid $color;
      z-index: 2;
    }
    
    .loader * {
      box-sizing: border-box;
    }
    $tire_size: 1;
    .wheel {
      width: 110px;
      height: 110px;
      border: 10px solid $color;
      border-radius: 50%;
      position: relative;
      animation: $speed rotate360 infinite linear;
      box-sizing: border-box;
      &:before {
        content: "";
        width: 100%;
        height: 100%;
        border: 12px dotted $color;
        border-radius: 50%;
        margin-left: -12px;
        margin-top: -12px;
        position: absolute;
      }
      &:after {
        content: "";
        width: 100%;
        height: 100%;
        border: 12px dotted $color;
        border-radius: 50%;
        margin-left: -12px;
        margin-top: -12px;
        position: absolute;
        transform: rotate(12deg);
      }
    }
    
    .spoke {
      top: 50%;
      transform: translateY(-50%);
      left: 50%;
      transform: translateX(-50%);
      position: absolute;
      &:before {
        left: 0;
        content: "";
        height: 1px;
        width: 45px;
        transform-origin: 20px center;
        background: rgba(0,0,0,.5);
        position: absolute;
        transform: rotate(10deg);
      }
      &:after {
        left: 0;
        content: "";
        height: 1px;
        width: 45px;
        transform-origin: 20px center;
        background: rgba(0,0,0,.5);
        position: absolute;
        transform: rotate(10deg);
        transform: rotate(-10deg);
      }
      &:nth-child(1) {
        transform: rotate(30deg);
      }
      &:nth-child(2) {
        transform: rotate(60deg);
      }
      &:nth-child(3) {
        transform: rotate(90deg);
      }
      &:nth-child(4) {
        transform: rotate(120deg);
      }
      &:nth-child(5) {
        transform: rotate(150deg);
      }
      &:nth-child(6) {
        transform: rotate(180deg);
      }
      &:nth-child(7) {
        transform: rotate(210deg);
      }
      &:nth-child(8) {
        transform: rotate(240deg);
      }
      &:nth-child(9) {
        transform: rotate(270deg);
      }
      &:nth-child(10) {
        transform: rotate(300deg);
      }
      &:nth-child(11) {
        transform: rotate(330deg);
      }
      &:nth-child(12) {
        transform: rotate(360deg);
      }
    }
    
    .crank {
      height: 4px;
      width: 30%;
      background: $color;
      transform-origin: left 1px;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translateY(-50%);
      .pedal {
        width: 20px;
        height: 6px;
        background: $color;
        position: absolute;
        right: -10px;
        margin-top: -1px;
        animation: $speed rotate360pedal infinite linear;
      }
      &.left {
        transform: rotate(180deg);
      }
    }
    
    @keyframes rotate360 {
      0% {
        transform: rotate(-50deg);
      }
    
      100% {
        transform: rotate(310deg);
      }
    }
    
    @keyframes rotate360pedal {
      0% {
        transform: rotate(410deg);
      }
    
      100% {
        transform: rotate(50deg);
      }
    }
}
25 Likes

I really, really love this. Because we have a large number of categories, our category list page takes a few seconds to load. The unicycle makes me smile!

5 Likes

Hi @Canapin,
Thank you for sharing us, I really like it. :heart: :slight_smile:
Is that possible to change the loader globally?

Thank you! :slight_smile:

If you’re talking about the small spinner, maybe start from this:

1 Like

Thank you @Canapin ,

I got it! I just target the spinner class and changed it to the pulser loader.

1 Like

how do i replace it with a gif. for example:

Not currently possible.

1 Like

Can I replace it with a static image? I probably won’t change it though