Back to Top

Floating button that appears when scrolling, allowing quick return to the page top

Overview

Back to Top provides a floating button that appears when users scroll down, allowing quick return to the page top. Perfect for long pages, documentation, and content-heavy sites.

Key Features

  • Auto Show/Hide - Appears after scrolling threshold
  • Smooth Scrolling - Configurable animation duration
  • Positioning - Bottom-right, bottom-left, or custom
  • Callbacks - onShow and onHide events

Basic Usage

const backToTop = Domma.elements.backToTop('#back-to-top', {
    showAfter: 300,
    duration: 600,
    position: 'bottom-right'
});

Getting Started

Add a back to top button to your page in three simple steps

Step 1: Add Button Element

Create a button element with a class and unique ID:

<button id="back-to-top" class="back-to-top-btn">↑</button>

Step 2: Include Domma

Add Domma CSS and JavaScript to your page:

<!-- CSS -->
<link rel="stylesheet" href="dist/domma.css">
<link rel="stylesheet" href="dist/elements.css">

<!-- JavaScript -->
<script src="dist/domma.min.js"></script>

Step 3: Initialize the Button

Call Domma.elements.backToTop() to activate the button:

Domma.elements.backToTop('#back-to-top', {
    showAfter: 300,   // Show after scrolling 300px
    duration: 500     // Scroll animation duration in ms
});

Note: The back to top button automatically shows/hides based on scroll position and smoothly animates scrolling to the top when clicked. The component handles all scroll event listeners and positioning.

Quick Start

Simple Back to Top Button

Simple Back to Top Button

Scroll down this demo area to see the button appear

Scroll down...

Keep scrolling...

Almost there...

You should see the button now!

<p class="text-center text-muted mb-3">Scroll down this demo area to see the button appear</p>
<div class="demo-scroll-container" id="quick-demo-container" style="height: 400px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 1200px;">
        <p><strong>Scroll down...</strong></p>
        <p class="mt-300">Keep scrolling...</p>
        <p class="mt-300">Almost there...</p>
        <p class="mt-200">You should see the button now!</p>
    </div>
    <button type="button" id="quick-back-to-top" class="btn btn-primary back-to-top-btn">↑</button>
</div>
/**
 * Simple Back to Top Button - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
    const quickBtn = E.backToTop(root.querySelector('#quick-back-to-top'), {
        showAfter: 200,
        duration: 500,
        target: '#quick-demo-container'
    });
}
<button id="back-to-top" class="back-to-top-btn">↑</button>

<script>
Domma.elements.backToTop('#back-to-top', {
    showAfter: 200,
    duration: 500
});
</script>

Native Web Component

Modern Standard: BackToTop is now available as a native Web Component with Shadow DOM encapsulation. Use either the traditional API (above) or the native <domma-back-to-top> tag below for better style isolation and framework compatibility.

Native Tag Syntax

Use the native <domma-back-to-top> Web Component directly in HTML with attributes for configuration:

<!-- Basic Web Component -->
<domma-back-to-top></domma-back-to-top>

<!-- Configured Web Component -->
<domma-back-to-top
    show-after="400"
    duration="500"
    position="bottom-left"
    offset="20"
></domma-back-to-top>

<!-- With Scroll Container -->
<domma-back-to-top
    target="#scrollable-content"
    show-after="300"
></domma-back-to-top>

Live Web Component Demo

Live Web Component Demo

Scroll down to see the native Web Component appear

Web Component Example

This uses <domma-back-to-top> tag

Shadow DOM provides style isolation

Reactive attribute updates supported

<p class="text-center text-muted mb-3">Scroll down to see the native Web Component appear</p>
<div class="demo-scroll-container" id="wc-demo-container" style="height: 400px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 1200px;">
        <p><strong>Web Component Example</strong></p>
        <p class="mt-200">This uses <code>&lt;domma-back-to-top&gt;</code> tag</p>
        <p class="mt-300">Shadow DOM provides style isolation</p>
        <p class="mt-300">Reactive attribute updates supported</p>
    </div>
</div>
<domma-back-to-top id="wc-demo-btn" show-after="200" duration="500" position="bottom-right" offset="16"></domma-back-to-top>
/**
 * Live Web Component Demo - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Web Component Demos
        // Main Web Component demo - get the element and set its target
    const wcDemoBtn = $(root).find('#wc-demo-btn')[0];
    if (wcDemoBtn) {
            wcDemoBtn.setAttribute('target', '#wc-demo-container');
        }
}

Available Attributes

Attribute Type Default Description
show-after Number viewport height Pixels to scroll before showing
duration Number 300 Scroll animation duration in ms
position String 'bottom-right' Position: bottom-right, bottom-left
offset Number 16 Distance from screen edge in px
target String window CSS selector for scroll container
z-index Number 1000 Z-index for stacking context
visible Boolean false Initial visibility state

JavaScript API

Access the Web Component instance and call methods programmatically:

// Get the Web Component element
const btn = $('domma-back-to-top')[0];

// Call methods
btn.scroll();              // Scroll to top
btn.show();                // Show button immediately
btn.hide();                // Hide button
btn.toggle();              // Toggle visibility
btn.isVisible();           // Check visibility → boolean
btn.getButton();           // Get button element

// Update attributes programmatically
btn.setShowAfter(500);     // Change threshold
btn.setPosition('bottom-left'); // Change position
btn.setOffset(24);         // Change offset

// Listen for events
btn.addEventListener('show', (e) => {
    console.log('Button appeared');
});

btn.addEventListener('hide', (e) => {
    console.log('Button hidden');
});

Reactive Attributes Demo

Reactive Attributes Demo

Attributes update reactively!

Try the buttons above

<div class="flex gap-2 justify-center mb-3">
    <button type="button" id="wc-change-position" class="btn btn-sm btn-outline">Toggle Position</button>
    <button type="button" id="wc-change-offset" class="btn btn-sm btn-outline">Change Offset</button>
    <button type="button" id="wc-manual-show" class="btn btn-sm btn-outline">Force Show</button>
</div>
<div class="demo-scroll-container" id="reactive-demo" style="height: 300px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 800px;">
        <p>Attributes update reactively!</p>
        <p class="mt-300">Try the buttons above</p>
    </div>
</div>
<domma-back-to-top id="reactive-btn" show-after="200" duration="400"></domma-back-to-top>
/**
 * Reactive Attributes Demo - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Reactive attributes demo
    const reactiveBtn = $(root).find('#reactive-btn')[0];
    if (reactiveBtn) {
            reactiveBtn.setAttribute('target', '#reactive-demo');

            let currentPosition = 'bottom-right';
            $(root).find('#wc-change-position').on('click', () => {
                currentPosition = currentPosition === 'bottom-right' ? 'bottom-left' : 'bottom-right';
                reactiveBtn.setPosition(currentPosition);
            });

            let currentOffset = 16;
            $(root).find('#wc-change-offset').on('click', () => {
                currentOffset = currentOffset === 16 ? 32 : 16;
                reactiveBtn.setOffset(currentOffset);
            });

            $(root).find('#wc-manual-show').on('click', () => {
                reactiveBtn.show();
            });
        }
}

Event Handling

<domma-back-to-top id="my-btn"></domma-back-to-top>

<script>
const btn = $('#my-btn')[0];

// Listen for show event
btn.addEventListener('show', (e) => {
    console.log('Button became visible');
    $('#status')[0].textContent = 'Visible';
});

// Listen for hide event
btn.addEventListener('hide', (e) => {
    console.log('Button became hidden');
    $('#status')[0].textContent = 'Hidden';
});
</script>

Benefits of Web Components

Shadow DOM Isolation: Styles don't leak in or out Native Browser API: No framework required Reactive Attributes: Changes update automatically Framework Agnostic: Works with React, Vue, Angular, Svelte Custom Events: Standard DOM event system Backwards Compatible: Traditional API still available

Using with Frameworks

React

import { useRef, useEffect } from 'react';

function MyComponent() {
    const btnRef = useRef(null);

    useEffect(() => {
        const btn = btnRef.current;

        const handleShow = () => console.log('Visible');
        btn.addEventListener('show', handleShow);

        return () => btn.removeEventListener('show', handleShow);
    }, []);

    return <domma-back-to-top ref={btnRef} show-after="300" />;
}

Vue

<template>
    <domma-back-to-top
        ref="backToTop"
        :show-after="300"
        :duration="500"
        @show="onShow"
        @hide="onHide"
    ></domma-back-to-top>
</template>

<script>
export default {
    methods: {
        onShow() { console.log('Visible'); },
        onHide() { console.log('Hidden'); }
    },
    mounted() {
        this.$refs.backToTop.scroll();
    }
};
</script>

Angular

import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
    selector: 'app-my-component',
    template: `
        <domma-back-to-top
            #backToTop
            show-after="300"
            (show)="onShow()"
            (hide)="onHide()"
        ></domma-back-to-top>
    `
})
export class MyComponent {
    @ViewChild('backToTop') backToTop!: ElementRef;

    onShow() { console.log('Visible'); }
    onHide() { console.log('Hidden'); }

    scrollToTop() {
        this.backToTop.nativeElement.scroll();
    }
}

CSS Part Customization

Customize Web Component styles using CSS ::part() selectors:

/* Target the button inside Shadow DOM */
domma-back-to-top::part(button) {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border: none;
    width: 50px;
    height: 50px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

domma-back-to-top::part(button):hover {
    transform: translateY(-2px) scale(1.15);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
}

Tutorial

Build a back to top button with custom styling and callbacks.

Tutorial

Implementation Guide

Scroll down to see the custom back to top button with callbacks tracking visibility state...

Understanding Back to Top

The back to top button provides a quick way to return to the page start without manually scrolling.

When to Use

Best for long-form content, documentation pages, blog articles, and dashboards with extensive data.

Customization Options

Configure appearance threshold, animation duration, positioning, and event callbacks to match your design.

User Experience

The button automatically shows when scrolling past the threshold and hides when near the top of the page.

Button status: Hidden

<div class="demo-scroll-container" id="tutorial-container" style="height: 400px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; position: relative;">
    <div style="min-height: 1500px; padding: 1.5rem;">
        <h3 class="text-2xl font-semibold mb-4">Implementation Guide</h3>
        <p class="mb-6 text-gray-600">Scroll down to see the custom back to top button with callbacks tracking visibility state...</p>
        <div class="mt-200">
            <h4>Understanding Back to Top</h4>
            <p>The back to top button provides a quick way to return to the page start without manually scrolling.</p>
        </div>
        <div class="mt-200">
            <h4>When to Use</h4>
            <p>Best for long-form content, documentation pages, blog articles, and dashboards with extensive data.</p>
        </div>
        <div class="mt-200">
            <h4>Customization Options</h4>
            <p>Configure appearance threshold, animation duration, positioning, and event callbacks to match your design.</p>
        </div>
        <div class="mt-200">
            <h4>User Experience</h4>
            <p>The button automatically shows when scrolling past the threshold and hides when near the top of the page.</p>
        </div>
    </div>
    <button type="button" id="tutorial-back-to-top" class="btn btn-primary back-to-top-btn">
        <span class="text-2xl">↑</span>
    </button>
</div>
<div id="tutorial-status" class="mt-3 p-3 bg-surface rounded text-center">
    <p class="text-muted text-sm">Button status: <span id="button-status">Hidden</span></p>
</div>
/**
 * Tutorial - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Tutorial
    const tutorialBtn = E.backToTop(root.querySelector('#tutorial-back-to-top'), {
            showAfter: 300,
            duration: 600,
            target: '#tutorial-container',
            onShow: () => {
                $(root).find('#button-status')[0].textContent = 'Visible';
            },
            onHide: () => {
                $(root).find('#button-status')[0].textContent = 'Hidden';
            }
        });
}
const backToTop = Domma.elements.backToTop('#back-to-top', {
    showAfter: 300,
    duration: 600,
    position: 'bottom-right',
    offset: { bottom: 20, right: 20 },
    target: '#scrollable-container',
    onShow: () => {
        console.log('Button shown');
        $('#status')[0].textContent = 'Visible';
    },
    onHide: () => {
        console.log('Button hidden');
        $('#status')[0].textContent = 'Hidden';
    }
});

Examples

Different Positions

Different Positions

Scroll to see buttons at different positions...

Bottom right button appears first

You should see the buttons now

<div class="flex gap-4 justify-center mb-3">
    <button type="button" id="show-bottom-right" class="btn btn-outline btn-sm">Bottom Right</button>
    <button type="button" id="show-bottom-left" class="btn btn-outline btn-sm">Bottom Left</button>
</div>
<div class="demo-scroll-container" id="position-demo" style="height: 300px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 800px;">
        <p>Scroll to see buttons at different positions...</p>
        <p class="mt-200">Bottom right button appears first</p>
        <p class="mt-300">You should see the buttons now</p>
    </div>
    <button type="button" id="btn-bottom-right" class="btn btn-primary back-to-top-btn">↑</button>
    <button type="button" id="btn-bottom-left" class="btn btn-primary back-to-top-btn">↑</button>
</div>
/**
 * Different Positions - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Position examples
    const btnRight = E.backToTop(root.querySelector('#btn-bottom-right'), {
            showAfter: 200,
            target: '#position-demo',
            offset: {bottom: 20, right: 20}
        });
    const btnLeft = E.backToTop(root.querySelector('#btn-bottom-left'), {
            showAfter: 200,
            target: '#position-demo',
            offset: {bottom: 20, left: 20}
        });
}

Custom Duration

Custom Duration

Try different scroll speeds...

Scroll down to see the button

<div class="demo-scroll-container" id="duration-demo" style="height: 300px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 1000px;">
        <p>Try different scroll speeds...</p>
        <div class="flex gap-2 mt-4">
            <button type="button" class="btn btn-sm btn-outline" data-action="1">Fast (200ms)</button>
            <button type="button" class="btn btn-sm btn-outline" data-action="2">Slow (1000ms)</button>
        </div>
        <p class="mt-300">Scroll down to see the button</p>
    </div>
    <button type="button" id="duration-btn" class="btn btn-primary back-to-top-btn">↑</button>
</div>
/**
 * Custom Duration - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Duration example
    const durationBtn = E.backToTop(root.querySelector('#duration-btn'), {
            showAfter: 200,
            duration: 600,
            target: '#duration-demo'
        });

    // Handlers the old page had inline (onclick=…), wired here.
    $(root).find('[data-action="1"]').on('click', function (event) {
        durationBtn.scroll(200);
    });
    $(root).find('[data-action="2"]').on('click', function (event) {
        durationBtn.scroll(1000);
    });
}

Programmatic Control

Programmatic Control

Use buttons above to control the back to top button

Scroll position tracked

<div class="flex gap-2 justify-center mb-3">
    <button type="button" id="prog-show" class="btn btn-outline btn-sm">Show</button>
    <button type="button" id="prog-hide" class="btn btn-outline btn-sm">Hide</button>
    <button type="button" id="prog-toggle" class="btn btn-outline btn-sm">Toggle</button>
    <button type="button" id="prog-scroll" class="btn btn-outline btn-sm">Scroll to Top</button>
</div>
<div class="demo-scroll-container" id="programmatic-demo" style="height: 300px; overflow-y: auto; border: 1px solid var(--dm-color-border, #e0e0e0); border-radius: 0.5rem; padding: 1.5rem; position: relative;">
    <div style="min-height: 800px;">
        <p>Use buttons above to control the back to top button</p>
        <p class="mt-600">Scroll position tracked</p>
    </div>
    <button type="button" id="programmatic-btn" class="btn btn-primary back-to-top-btn">↑</button>
</div>
/**
 * Programmatic Control - from public/showcase/elements/back-to-top.
 */
export function mount(root) {
        // Programmatic control
    const programmaticBtn = E.backToTop(root.querySelector('#programmatic-btn'), {
            showAfter: 200,
            target: '#programmatic-demo'
        });
    $(root).find('#prog-show').on('click', () => programmaticBtn.show());
    $(root).find('#prog-hide').on('click', () => programmaticBtn.hide());
    $(root).find('#prog-toggle').on('click', () => programmaticBtn.toggle());
    $(root).find('#prog-scroll').on('click', () => programmaticBtn.scroll(400));
}

Best Practices

Accessibility

  • Keyboard Access - Ensure button is keyboard focusable
  • ARIA Label - Add aria-label="Back to top" for screen readers
  • Focus Visibility - Provide clear focus indicator

UX Guidelines

  • Threshold - Show after 200-400px scroll for optimal UX
  • Positioning - Bottom-right is conventional, but avoid blocking content
  • Animation Duration - 400-800ms provides smooth but quick scrolling
  • Visual Clarity - Use clear up arrow (↑) or icon

Performance

  • Throttle Scroll Events - Component automatically throttles scroll listeners
  • Destroy When Done - Call destroy() on single-page apps when navigating

API Reference

Option Type Default Description
showAfter Number 300 Scroll pixels before showing button
duration Number 500 Scroll animation duration (ms)
position String 'bottom-right' Position: bottom-right, bottom-left
offset Object { bottom: 20, right: 20 } Offset from edges (px)
target String/Element window Scroll target (default: window)
onShow Function null Show callback
onHide Function null Hide callback

Methods

Method Parameters Description
scroll(duration) duration (optional) Scroll to top with optional custom duration
show() - Show button immediately
hide() - Hide button immediately
toggle() - Toggle button visibility
isVisible() - Check if button is visible
getButton() - Get button element
destroy() - Remove button and clean up

Config Engine

$.setup({
    '#back-to-top': {
        component: 'backToTop',
        options: {
            showAfter: 400,
            duration: 600,
            position: 'bottom-right',
            onShow: () => console.log('Button appeared'),
            onHide: () => console.log('Button hidden')
        }
    }
});

CSS Customisation

Override these CSS variables to customise Back-to-Top appearance and match your design system.

Variable Default Controls
--dm-primary var(--dm-blue-600) Button background colour
--dm-primary-hover var(--dm-blue-700) Button hover colour
--dm-radius-full 9999px Button shape (circular)
--dm-shadow-md 0 4px 6px... Button shadow

Example Override

:root {
    /* Square button */
    --dm-radius-full: 0.5rem;
}

Full CSS Customisation Cheat-Sheet →

Related Elements

Related Elements
<div class="grid grid-cols-1 gap-4"><a href="/showcase/elements/navbar" class="card card-hover">
        <div class="card-body">
            <h3 class="card-title">Navbar</h3>
            <p class="card-text">Sticky navigation for page sections</p>
        </div>
    </a>
    <a href="/showcase/elements/breadcrumbs" class="card card-hover">
        <div class="card-body">
            <h3 class="card-title">Breadcrumbs</h3>
            <p class="card-text">Navigation trail for deep content</p>
        </div>
    </a>
</div>