Newsletter

Hear about new files and fixes

I send an email when I publish something new or release a fix for a file you might already use. There’s no fixed schedule.

Every email has a one-click unsubscribe link, and none of them contain tracking pixels.

Etch component

Back to Top

A native button that takes visitors back to the top. Its progress ring follows the button’s shape, it can appear only on the way up, it stays clear of footers and cookie bars, and it moves keyboard focus to the start of the page. Everything is set in the Etch props panel.

Download .jsonSee the demo

Paste it on the Etch canvas with Ctrl+V or ⌘V, then click Save.

Stay up to date. Etch won’t update a pasted component for you. Subscribe and get one email when a new version is out.

Free on unlimited personal, client and commercial sites. Modify it however you like.License: FSL-1.1-MIT
Version
1.0.0-beta.5Changelog
Options
21 settings+ 41 CSS variablesSee all
Tested on Etch
1.7.1
Tested on WordPress
7.1.3
Live demo

One corner button per page

Scroll the demo: the corner button fills its ring, rises above the cookie notice and the footer, and brings you back. Three scroll panels have buttons of their own. Switch to Mobile for a phone width.

Live
  • Instances: 5
  • Positions: corner, inline
  • Scroll panels: 3
  • Reveal: slide, fade, scale
Open in a new tab
Features

What it does

One pasted component and no setup: no library, no icon font and no external script. The CSS and the script travel with it. In the builder the button stays on screen, so every setting you change shows at once.

  • A ring that follows the shape

    The progress ring fills as the page scrolls and traces the button’s outline: a circle, a pill with text, a rounded square, even a different radius on each corner.

  • Shows up when it helps

    It appears after the scroll distance you set. It can show only when the visitor scrolls back up, or fade out after a few seconds without scrolling. A focused button always stays visible.

  • Stays clear of footers and cookie bars

    Name the elements it must not cover. It moves above them, or hides while they overlap it, and follows banners that appear, resize or slide in without waiting for a scroll.

  • Accessible by default

    A native button with a 44 px minimum target and a visible focus ring. While hidden, it is disabled and out of the accessibility tree; Auto-hide only fades it out, so Tab and screen readers still reach it. After the return, focus moves to the main heading.

  • Icon, text or your own content

    Chevron, arrow or any SVG from the Media Library, with or without text, in a row or a column. Or fill the content slot with your own text, images and decorative SVG.

  • Smooth, eased or instant

    The browser’s smooth scrolling, or an eased scroll of a set duration. Reduced motion always returns at once, and any wheel, touch, pointer or key input stops a return.

  • Works with any scroll setup

    The page, themes that scroll the body, or a panel with its own scrollbar. Events let a smooth-scroll library such as Lenis take over the return.

  • 41 styling variables

    Size, colors, border, radius, shadow, ring, hover, focus and the animation, set on any parent class. ACSS 4 tokens with plain fallbacks, so it works without Automatic.css too.

Install

Add it in four steps

No plugin and no build step. The component brings its own CSS and script and loads nothing from a CDN. It works with or without Automatic.css 4.

Shows without its styles? It was pasted but not saved, and pasting again won’t repair it. Delete ED Back to Top from your components, paste it again and click Save.

  1. Copy the componentUse Copy component at the top of this page, or download the .json file.
  2. Paste it once per pageSelect a footer template or the end of the page, so every page gets one button, then press Ctrl+V or ⌘V and click Save. Etch adds the component to your list right away but stores its CSS only when you save.
  3. Name it in your languageIn the Button group, set ARIA label in your site language, such as Powrót na górę. It names the button when it shows only an icon.
  4. Save and scrollIn the builder the button stays on screen, so every setting you change shows at once. On the page it appears after 100 px of scrolling.
Options

21 settings in the Etch props panel

Builder preview comes first, then four groups as in the Etch props panel, where each setting has help text too. Nothing is required: the defaults give a round progress button in the bottom corner.

Numbers are text fields

Etch has no number field yet, so Show after, Auto-hide and Scroll time are text fields. Type a plain number: pixels for Show after, milliseconds for the other two. Anything else uses the default.

Clearing a text field restores its default. Avoid elements, Scroll container and Focus target take CSS selectors as plain text.

Builder

Builder previewpreview
Builder canvas only. Visible keeps the button on screen while you edit, Follow scroll uses the page rules, so you scroll the canvas to reveal it, and Hidden hides it while you edit what is underneath. The page itself always follows the scroll rules.Default: Visible

Button

Button typebutton.buttonType
Progress circle draws a ring that fills as the page scrolls and follows the button’s shape. Regular is a plain button.Default: Progress circle
Contentbutton.contentMode
Icon / text uses the settings below. Custom content shows the content slot: text, images and decorative SVG only, no links or buttons. An empty slot falls back to the icon and text.Default: Icon / text
Button textbutton.buttonText
Visible text next to the icon, such as Top. It also names the button, unless ARIA label contains the same words.Empty by default.
ARIA labelbutton.ariaLabel
What screen readers announce for a button that shows only an icon. Write it in your site language. With visible text it is used only when it contains that text.Default: Back to top
Iconbutton.icon
Chevron, Arrow, Custom SVG or None. The icon is decorative and never part of the name.Default: Chevron
SVG filebutton.customIcon
Used when Icon is Custom SVG: pick an SVG in Etch’s Select Media dialog or paste its URL. Etch inlines the file, so the browser makes no extra request.Empty by default.
SVG colorsbutton.customIconColors
Use text color turns the SVG’s colors into the button’s text color, so the icon color and hover apply. Keep original colors suits multicolor icons.Default: Use text color
Inner layoutbutton.layout
Row, Reversed row or Column for the icon, the text and custom content.Default: Row
Ring line capbutton.lineCap
Round or Square ends of the progress line.Default: Round

Visibility

Positionvisibility.position
Bottom end, Bottom start or Bottom center of the screen, following the page direction. Inline keeps it in the page flow, for example at the end of an article or in a sticky bar.Default: Bottom end
Show after (px)visibility.visibleAfter
How far the visitor scrolls before the button appears, from 0 to 100000.Default: 100
Scroll-up onlyvisibility.onlyScrollingUp
Yes shows the button only after the visitor scrolls back up a little. Scrolling down hides it again.Default: No
Auto-hide (ms)visibility.idleHide
Fades the button out after this many milliseconds without scrolling, from 500 to 60000. Hover and keyboard focus keep it visible, and Tab and screen readers still reach it. Empty or 0 turns it off.Empty by default.
Animationvisibility.reveal
Slide, Fade, Scale, None or Custom transform for showing and hiding. Reduced motion turns it off.Default: Slide

Overlap

Avoid elementsoverlap.avoidSelector
CSS selectors of elements the button must not cover, such as footer, .cookie-banner. Only elements below the button in its column count. Empty turns this off.Empty by default.
On overlapoverlap.avoidMode
Move above lifts the button over those elements, and hides it when there is no room left. Hide hides it while they overlap it.Default: Move above

Scrolling

Scroll behaviorscrolling.scrollBehavior
Smooth or Instant return. With reduced motion the return is always instant.Default: Smooth
Scroll time (ms)scrolling.scrollDuration
Empty uses the browser’s smooth scrolling. 100 to 5000 gives an eased scroll of that length.Empty by default.
Scroll containerscrolling.scrollContainer
Empty uses the page and detects themes that scroll the body. A selector, such as #app-main, uses that element instead.Empty by default.
Focus targetscrolling.focusTarget
Where keyboard focus goes after the return. By default the main heading, then main, then the page. For a scroll panel, enter a heading inside it.Empty by default.
Events: 3 for developers

The before and complete events bubble from the button, so one listener on document handles every button.

Events

ed-back-to-top:before
Fires on activation, before the scroll starts, and can be canceled. Call preventDefault() and start your own scroll, for example with Lenis; the button still moves focus and fires complete.Detail: behavior, duration, container
ed-back-to-top:complete
Fires once when the top is reached and focus has moved. A return stopped by the visitor or by another script’s scroll does not fire it.Detail: none
ed-back-to-top:refresh
You dispatch it on document after changes the button cannot see, such as tokens changed on a parent or a scroll container added later. Every button measures itself again.Detail: none
Styling: 41 CSS variables

Set them on any parent element or class, such as the footer, or on :root for the whole site. They all inherit, so no extra selectors or !important are needed. The component’s own style also lists them as commented lines you can switch on.

Placement

--ed-back-to-top-display
Set none, for example in a media query, to hide the button on some screens.Default: inline-flex
--ed-back-to-top-offset-inline
Distance from the side edge for Bottom start and Bottom end.Default: var(--space-s, 1rem)
--ed-back-to-top-offset-block
Distance from the bottom edge.Default: var(--space-s, 1rem)
--ed-back-to-top-z-index
Stacking layer of the fixed button.Default: 50

Size and surface

--ed-back-to-top-size
Minimum width and height; never below 44 px.Default: 60px
--ed-back-to-top-padding
Inner spacing.Default: 0.75rem
--ed-back-to-top-gap
Space between the icon, the text and custom content.Default: var(--space-xs, 0.5rem)
--ed-back-to-top-bg
Background.Default: var(--bg-ultra-light, #ffffff)
--ed-back-to-top-color
Text and icon color.Default: var(--text-dark, #17212b)
--ed-back-to-top-border-width
Border width; 0 removes the border.Default: 1px
--ed-back-to-top-border-style
Border style.Default: solid
--ed-back-to-top-border-color
Border color.Default: var(--neutral-light, #cbd5e1)
--ed-back-to-top-radius
Corner radius, also per corner or with calc(). The progress ring follows it.Default: 999px
--ed-back-to-top-shadow
Box shadow.Default: 0 3px 16px rgb(0 0 0 / 0.08)
--ed-back-to-top-backdrop-filter
Backdrop filter, such as blur(8px) with a translucent background.Default: none

Text

--ed-back-to-top-font-family
Font of the button text.Default: inherit
--ed-back-to-top-text-size
Text size.Default: var(--text-m, 1rem)
--ed-back-to-top-font-weight
Text weight.Default: inherit
--ed-back-to-top-line-height
Line height of wrapped text.Default: 1.2
--ed-back-to-top-letter-spacing
Letter spacing.Default: normal
--ed-back-to-top-text-transform
Text transform, such as uppercase.Default: none

Icon

--ed-back-to-top-icon-size
Size of the icon box.Default: 1.25rem
--ed-back-to-top-icon-color
Icon color; follows the text color by default.Default: currentColor
--ed-back-to-top-icon-thickness
Stroke width of the built-in chevron and arrow, on a 24-unit grid.Default: 2

Progress ring

--ed-back-to-top-ring-width
Width of the progress line.Default: 2px
--ed-back-to-top-ring-inset
Gap between the border and the ring.Default: 0px
--ed-back-to-top-track-color
The ring’s track.Default: var(--neutral-light, #cbd5e1)
--ed-back-to-top-progress-color
Scroll progress.Default: var(--primary, #245be0)

Hover, press and focus

--ed-back-to-top-hover-bg
Hover background. By default the background, mixed 10% toward the text color, so a dark button stays dark.Default: bg 90%, color 10%
--ed-back-to-top-hover-color
Hover text and icon color.Default: --ed-back-to-top-color
--ed-back-to-top-hover-border-color
Hover border color.Default: --ed-back-to-top-border-color
--ed-back-to-top-hover-shadow
Hover shadow.Default: --ed-back-to-top-shadow
--ed-back-to-top-hover-progress-color
Hover progress color.Default: --ed-back-to-top-progress-color
--ed-back-to-top-active-transform
Pressed state, such as scale(0.95). Off with reduced motion.Default: none
--ed-back-to-top-focus-color
Keyboard focus outline color.Default: var(--focus-color, var(--primary, #245be0))
--ed-back-to-top-focus-width
Focus outline width.Default: 3px
--ed-back-to-top-focus-offset
Focus outline offset.Default: 4px

Reveal

--ed-back-to-top-transition
Duration and easing of the show, hide and hover transitions.Default: var(--transition, 0.2s ease)
--ed-back-to-top-hidden-offset
Slide distance of the Slide animation.Default: 10px
--ed-back-to-top-hidden-scale
Starting scale of the Scale animation.Default: 0.8
--ed-back-to-top-hidden-transform
Hidden state of the Custom transform animation.Default: translateY(10px)
Good to know

Before you place it

Things that are easy to miss when the button goes onto a real site.

  • One button per page. Paste it into a footer template or at the end of the page. Two fixed buttons in the same corner would sit on top of each other.
  • Keep it out of transformed parents. A parent with transform, filter or contain changes how a fixed button is placed, so don’t put it inside an animated wrapper.
  • Scroll-up only hides it from some screen reader users. Someone who reads without scrolling reaches the button only while it shows, so leave it off when the button should always be there after the threshold. Auto-hide only fades the button out and keeps it in the reading order.
  • Custom content stays noninteractive. The slot sits inside a native button: text, images and decorative SVG only, no links, buttons or form fields. Give an image-only slot alt="" and set ARIA label.
  • Smooth-scroll libraries can take over. With Lenis or GSAP ScrollSmoother, cancel ed-back-to-top:before and start your own scroll. The button still moves focus and fires complete.
  • Tell it about late changes. After adding a scroll container later, or changing its variables on a parent, dispatch ed-back-to-top:refresh on document.
  • Not tested yet: NVDA and VoiceOver on iOS. It was checked in Chrome, Firefox, Safari and Microsoft Edge, with VoiceOver on macOS, TalkBack on an Android emulator and Safari in the iOS Simulator.
License

Free to use. Not for resale.

Licensed under FSL-1.1-MIT, the Functional Source License with an MIT future license. Each version becomes MIT-licensed two years after its release.

You can
  • Use it on unlimited personal, client and commercial websites
  • Modify and adapt it however you like
  • Use it in paid design and development work for your clients
You can’t
  • Sell it, or a modified version, as a component: in a library, bundle, template kit, plugin or marketplace listing
  • Offer it in another commercial product or service that competes with it

Keep the copyright and license notices in the code, and link to this page instead of re-hosting the download, so everyone gets fixes and updates. Read the full license

Third-party code

None. This component is all my own code under FSL-1.1-MIT. It bundles no library and loads no icon font or external script.

Updates

How to update

Etch has no update button for pasted components. Pasting a newer version over an installed one changes nothing: Etch keeps the component and the CSS it already has. To update, reinstall in five steps.

  1. Note what you setWrite down the settings of each button and copy any CSS you changed in the component’s own styles.
  2. Delete the componentIn Etch, delete ED Back to Top from your components. The button disappears from every page that used it.
  3. Delete its stylesIn the Style Manager, search for ed-back-to-top and delete the ten styles listed below.
  4. Paste the new versionCopy or download the new JSON from this page, paste it on the canvas and click Save. Etch creates the component and its CSS fresh.
  5. Put your buttons backInsert the button wherever it was, usually in a footer template, re-enter its settings and save. Reload any other open builder tab.

Styles to delete in step 3

  • .ed-back-to-top
  • .ed-back-to-top__progress
  • .ed-back-to-top__track
  • .ed-back-to-top__value
  • .ed-back-to-top__content
  • .ed-back-to-top__icon
  • .ed-back-to-top__chevron
  • .ed-back-to-top__arrow
  • .ed-back-to-top__custom-icon
  • .ed-back-to-top__text

Trade-offs

  • Placements are not kept. Etch doesn’t link old buttons to the new component, so you insert each one again and re-enter its settings.
  • Edits inside the component’s CSS are replaced. Keep overrides on a parent class with the --ed-back-to-top-* variables; those survive an update.
  • Skipping a step leaves a mix. Without step 2, Etch keeps the old component. Without step 3, the new version runs on the old CSS.

Coming from 1.0.0-beta.4? Follow the five steps above. Settings, classes and styling variables are unchanged, so you can re-enter your settings as they were. With Automatic.css, button text now uses the component’s text defaults (see the changelog). Subscribe and you’ll get one email when the next version is out.

Changelog

What changed

Versions follow semantic versioning. When an update changes how existing options behave, its entry says so.

1.0.0-beta.5Latest
  • FixedWith reduced motion on a site that uses Automatic.css, Safari and other WebKit browsers kept the English fallback name “Back to top” on a button with visible text until the next scroll, so voice control could miss it. The name now follows the visible text as soon as it shows.
  • FixedOn Automatic.css sites the font family, font weight, line height, letter spacing and text transform variables did nothing, because ACSS styles every plain button with a stronger rule. They now work there too, on the page and in the builder.
  • FixedAn empty Builder preview, for example from a site component that passes it down without a value, hid the button in the canvas. It now counts as Visible.
  • ChangedOn Automatic.css sites, button text now uses the component’s text defaults, such as line height 1.2, instead of your ACSS button text settings. To keep those, set --ed-back-to-top-line-height: var(--btn-line-height) and the other text variables from the matching --btn-* variables on :root.
  • ChangedThe script and the CSS of .ed-back-to-top changed, so update the component and its styles together (see How to update). Props, classes and the 41 styling variables are the same.
  • TestedChrome, Firefox, Microsoft Edge and WebKit on the desktop, with and without reduced motion, and Safari on iPhone in the iOS Simulator with Reduce Motion, on Etch 1.7.1 with Automatic.css. It stays a beta until NVDA and VoiceOver on a real iPhone are tested.
1.0.0-beta.4
  • ChangedAuto-hide now only fades the button out. It stays enabled and in the reading order, so Tab and screen readers still reach it, and it shows as soon as it takes focus. Clicks and taps on its spot reach the page, and it still moves above avoided elements.
  • FixedWith TalkBack resting on the button, Auto-hide hid it about 2.6 s after a scroll: TalkBack lost its place, read the page from the top, and a double tap did nothing. TalkBack now stays on the faded button, and a double tap returns to the top.
  • FixedOn Android phones, one tap on the button stopped Auto-hide until the visitor tapped somewhere else, because Chrome keeps the hover state after a tap. Hover now counts only for a mouse or a pen.
  • ChangedAn auto-hidden button has the new state idle, so site CSS that targets [data-ed-btt-state="hidden"] no longer matches it. Below the threshold, under Scroll-up only and when avoided elements hide it, the button is hidden as before.
  • ChangedThe script and the CSS of .ed-back-to-top changed, so update the component and its styles together (see How to update). Props, classes and the 41 styling variables are the same.
  • TestedTalkBack on Android, Safari on iPhone and iPad in the iOS Simulator, VoiceOver with Safari on macOS, and Chrome, Firefox, Microsoft Edge and WebKit on the desktop. It stays a beta until NVDA and VoiceOver on a real iPhone are tested.
1.0.0-beta.3
  • ReleasedFirst public version. It stays a beta until NVDA, iOS and Android devices and Microsoft Edge are tested.
Feedback

Tell me what this component needs

A bug, a missing option, or anything that made the component awkward to use in a real build — it all belongs here. The versions above are the ones it was tested on; if yours differ, say so.