Loading

Page or container loading indicators.

Class name Type
.prs-loading Component Container
.prs-loading-reverse Modifier Light on dark
.prs-loading-xs Modifier Size
.prs-loading-sm Modifier Size
.prs-loading-md Modifier Size
.prs-loading-lg Modifier Size
.prs-loading-xl Modifier Size
  • Default

    Use the default SVG when placing it over a light colors background. The animation falls back to fade when reduce motion OS setting is applied.

    <span class="prs-loading">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
        <style>
          @scope {
            svg:has(.spinner) { animation: fader 2s ease infinite; }
            
            .spinner-burst { fill: #FFF; transform-origin: 100px 100px; }
            .spinner-center { fill: #000;}
            .spinner-shape-1 { fill: var(--color-1, #00BCFF); }
            .spinner-shape-2 { fill: var(--color-2, #FF2986); }
            .spinner-shape-3 { fill: var(--color-3, #0900DA); }
    
            @media (prefers-reduced-motion: no-preference) {
              svg:has(.spinner) { animation: none; }
              .spinner-burst { animation: spinner 10s linear infinite; }
            }
    
            @keyframes spinner {
              from { transform: rotate(0deg); }
              to { transform: rotate(360deg); }
            }
    
            @keyframes fader {
              50% { opacity: 0.5; }
            }
          }
        </style>
    
        <defs>
          <mask id="spinner-mask">
            <polygon class="spinner-burst" points="200 103.43 199.98 96.06 132.69 96.23 198.26 81.09 196.6 73.91 131.03 89.05 191.59 59.7 188.37 53.07 127.82 82.42 180.32 40.33 175.71 34.59 123.21 76.67 165.03 23.96 159.26 19.38 117.44 72.09 146.48 11.39 139.83 8.21 110.79 68.91 125.6 3.27 118.41 1.65 103.6 67.29 103.43 0 96.06 .02 96.23 67.31 81.09 1.74 73.91 3.4 89.05 68.97 59.7 8.41 53.07 11.63 82.42 72.18 40.33 19.68 34.59 24.29 76.67 76.79 23.96 34.97 19.38 40.74 72.09 82.56 11.39 53.52 8.21 60.17 68.91 89.21 3.27 74.4 1.65 81.59 67.29 96.4 0 96.57 .02 103.94 67.31 103.77 1.74 118.91 3.4 126.09 68.97 110.95 8.41 140.3 11.63 146.93 72.18 117.58 19.68 159.67 24.29 165.42 76.79 123.33 34.97 176.04 40.74 180.62 82.56 127.91 53.52 188.61 60.17 191.79 89.21 131.09 74.4 196.73 81.59 198.35 96.4 132.71 96.57 200 103.94 199.98 103.77 132.69 118.91 198.26 126.09 196.6 110.95 131.03 140.3 191.59 146.93 188.37 117.58 127.82 159.67 180.32 165.41 175.71 123.33 123.21 176.04 165.03 180.62 159.26 127.91 117.44 188.61 146.48 191.79 139.83 131.09 110.79 196.73 125.6 198.35 118.41 132.71 103.6 200 103.43" />
            <circle class="spinner-center" cx="100" cy="100" r="36" />
          </mask>
        </defs>
    
        <g class="spinner" mask="url(#spinner-mask)">
          <path class="spinner-shape-1" d="M69.13,24.54c-28.53,7.23-53.33,125.75-16.96,142.83,37.3,17.52,98.5-4.67,111.3-39.04,15.97-42.87-68.32-110.39-94.34-103.79Z" />
          <path class="spinner-shape-2" d="M134.03,159.16s-23.38,11.77-42.45-2.6c-19.06-14.37-141.52-88.35-19.47-107.14,26.27-4.05,32.13-31.14,73.35-16.7,25.79,9.04,44.25,102.01-11.43,126.44Z" />
          <path class="spinner-shape-3" d="M163.47,128.33c12.23-31.33-28.29-73.76-61.41-93.56-9.4,5.42-17.32,12.7-29.95,14.65-10.41,1.6-19.03,3.61-26.1,5.94-6.24,16.2-10.65,36.53-11.75,55.54,18.72,20.34,48.67,39.14,57.32,45.66,19.06,14.37,42.45,2.6,42.45,2.6,1.03-.45,2.03-.93,3.01-1.43,12.43-7.74,21.97-17.95,26.43-29.4Z" />
        </g>
    
      </svg>
    </span>
    
  • Full Working Example (dark background)

    Toggle the button to see a full working example with transition and styles applied. Please use the --prs-c-gray-900 color to to 80% opacity and a backdrop-filter: blur(8px); as well as cursor: wait on a backdrop overlay. The status panel should fade in and then out for 500ms each.

    Make sure to adjust the status text to indicate what we are waiting for.

    <div x-data="
      {
        saving: false,
        flash(duration = 500) {
          this.saving = true
          setTimeout(() => {
            this.saving = false
          }, duration)
        },
      }
    ">
    
      <button @click="flash(5000)" class="prs-btn prs-btn-primary">Save</button>
    
      <!-- ⚠️ notice the aria attributes ⚠️ -->
      <div role="status" aria-live="polite" aria-atomic="true" x-show="saving" x-transition.opacity.duration.500ms class="backdrop-blur-sm flex items-center justify-center fixed inset-0 z-50 cursor-wait select-none before:(content-[''] [background-color:var(--prs-c-gray-900)] absolute inset-0 z-[-1] opacity-80)" x-cloak>
        <span class="prs-loading prs-loading-sm prs-loading-reverse">
          <span class="sr-only">Saving...</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
              <style>
                @scope {
                  svg:has(.spinner) { animation: fader 2s ease infinite; }
                  
                  .spinner-burst { fill: #FFF; transform-origin: 100px 100px; }
                  .spinner-center { fill: #000;}
                  .spinner-shape-1 { fill: var(--color-1, #00BCFF); }
                  .spinner-shape-2 { fill: var(--color-2, #FF2986); }
                  .spinner-shape-3 { fill: var(--color-3, #0900DA); }
    
                  @media (prefers-reduced-motion: no-preference) {
                    svg:has(.spinner) { animation: none; }
                    .spinner-burst { animation: spinner 10s linear infinite; }
                  }
    
                  @keyframes spinner {
                    from { transform: rotate(0deg); }
                    to { transform: rotate(360deg); }
                  }
    
                  @keyframes fader {
                    50% { opacity: 0.5; }
                  }
                }
              </style>
    
              <defs>
                <mask id="spinner-mask">
                  <polygon class="spinner-burst" points="200 103.43 199.98 96.06 132.69 96.23 198.26 81.09 196.6 73.91 131.03 89.05 191.59 59.7 188.37 53.07 127.82 82.42 180.32 40.33 175.71 34.59 123.21 76.67 165.03 23.96 159.26 19.38 117.44 72.09 146.48 11.39 139.83 8.21 110.79 68.91 125.6 3.27 118.41 1.65 103.6 67.29 103.43 0 96.06 .02 96.23 67.31 81.09 1.74 73.91 3.4 89.05 68.97 59.7 8.41 53.07 11.63 82.42 72.18 40.33 19.68 34.59 24.29 76.67 76.79 23.96 34.97 19.38 40.74 72.09 82.56 11.39 53.52 8.21 60.17 68.91 89.21 3.27 74.4 1.65 81.59 67.29 96.4 0 96.57 .02 103.94 67.31 103.77 1.74 118.91 3.4 126.09 68.97 110.95 8.41 140.3 11.63 146.93 72.18 117.58 19.68 159.67 24.29 165.42 76.79 123.33 34.97 176.04 40.74 180.62 82.56 127.91 53.52 188.61 60.17 191.79 89.21 131.09 74.4 196.73 81.59 198.35 96.4 132.71 96.57 200 103.94 199.98 103.77 132.69 118.91 198.26 126.09 196.6 110.95 131.03 140.3 191.59 146.93 188.37 117.58 127.82 159.67 180.32 165.41 175.71 123.33 123.21 176.04 165.03 180.62 159.26 127.91 117.44 188.61 146.48 191.79 139.83 131.09 110.79 196.73 125.6 198.35 118.41 132.71 103.6 200 103.43" />
                  <circle class="spinner-center" cx="100" cy="100" r="36" />
                </mask>
              </defs>
    
              <g class="spinner" mask="url(#spinner-mask)">
                <path class="spinner-shape-1" d="M69.13,24.54c-28.53,7.23-53.33,125.75-16.96,142.83,37.3,17.52,98.5-4.67,111.3-39.04,15.97-42.87-68.32-110.39-94.34-103.79Z" />
                <path class="spinner-shape-2" d="M134.03,159.16s-23.38,11.77-42.45-2.6c-19.06-14.37-141.52-88.35-19.47-107.14,26.27-4.05,32.13-31.14,73.35-16.7,25.79,9.04,44.25,102.01-11.43,126.44Z" />
                <path class="spinner-shape-3" d="M163.47,128.33c12.23-31.33-28.29-73.76-61.41-93.56-9.4,5.42-17.32,12.7-29.95,14.65-10.41,1.6-19.03,3.61-26.1,5.94-6.24,16.2-10.65,36.53-11.75,55.54,18.72,20.34,48.67,39.14,57.32,45.66,19.06,14.37,42.45,2.6,42.45,2.6,1.03-.45,2.03-.93,3.01-1.43,12.43-7.74,21.97-17.95,26.43-29.4Z" />
              </g>
    
            </svg>
        </span>
      </div>
    
    </div>
    

CSS

Full Library

Component Only

.prs-loading {
  pointer-events: none;
  aspect-ratio: 1;
  vertical-align: middle;
  width: calc(var(--size-selector, .625rem) * 6);
  display: inline-block;

  &:where(.prs-loading-reverse) svg {
    --color-1: #00BCFF;
    --color-2: #FF2986;
    --color-3: #FFF;
  }

  &:where(.prs-loading-xs) {
    width: calc(var(--size-selector, .625rem) * 2);
  }
  &:where(.prs-loading-sm) {
    width: calc(var(--size-selector, .625rem) * 4);
  }
  &:where(.prs-loading-md) {
    width: calc(var(--size-selector, .625rem) * 6);
  }
  &:where(.prs-loading-lg) {
    width: calc(var(--size-selector, .625rem) * 8);
  }
  &:where(.prs-loading-xl) {
    width: calc(var(--size-selector, .625rem) * 10);
  }
}

Coming soon...