<a href="#" class="a-progress-button" data-progress-button>
	<svg class="a-progress-button__svg">
		<circle class="a-progress-button__indicator" r="15" cx="16" cy="16" />
	</svg>
	<svg class="a-icon" role="img" title="">
		<use xlink:href="#close" />
	</svg>
</a>
<a href="{{ buttonUrl }}" class="a-progress-button" data-progress-button>
	<svg class="a-progress-button__svg">
		<circle class="a-progress-button__indicator" r="15" cx="16" cy="16" />
	</svg>
	{{> @a-icon name="close" }}
</a>
  • Content:
    $buttonWidth: 32px; // NOTE: If you change this value you need to change the r, cx & cy attributes in the markup
    $stroke-width: 2px;
    $transition-duration: 100ms;
    $svg-radius: ($buttonWidth / 2) - ($stroke-width / 2);
    $offset: $buttonWidth / 2;
    $circle-circumference: 2 * 3.14159 * $svg-radius; // 2 * π * R  = C
    
    .a-progress-button {
    	z-index: 5;
    	display: block;
    	position: fixed;
    	top: 52px;
    	right: 20px;
    	background-color: white;
    	border-radius: 50%;
    	width: $buttonWidth;
    	height: $buttonWidth;
    		box-shadow: 0 1px 7px 0 rgba(0, 0, 0, 0.1);
    
    	@include media($sm-breakpoint) {
    		display: none;
    	}
    
    	&__svg {
    		transform: rotate(-90deg);
    		width: 100%;
    		height: 100%;
    	}
    
    	&__indicator {
    		fill: $color-text-negative;
    		stroke: $color-primary;
    		stroke-width: 2px;
    		stroke-dasharray: $circle-circumference;
    		stroke-dashoffset: $circle-circumference;
    		transition: stroke-dashoffset $transition-duration;
    	}
    
    	.a-icon {
    		position: absolute;
    		fill: $color-primary;
    		display: block;
    		width: $buttonWidth / 2;
    		height: $buttonWidth / 2;
    		top: 50%;
    		left: 50%;
    		transform: translate(-50%,-50%);
    	}
    }
    
    .a-progress-button {
    	opacity: 1;
    	transition: opacity $transition-fast ease;
    
    	&--hidden {
    		opacity: 0;
    	}
    }
    
  • URL: /components/raw/a-progress-button/_a-progress-button.scss
  • Filesystem Path: src/components/atoms/a-progress-button/_a-progress-button.scss
  • Size: 1.2 KB
  • Content:
    import BaseView from "base-view";
    import throttle from "lodash.throttle";
    
    const PROGRESS_INDICATOR_SELECTOR = '.a-progress-button__indicator';
    
    const HIDDEN_STATE = 'a-progress-button--hidden';
    
    export default class ProgressButton extends BaseView {
    
    	constructor(element) {
    		super(element);
    
    		this.article = document.querySelector('[data-article-progress]');
    		this.progressIndicator = this.element.querySelector(PROGRESS_INDICATOR_SELECTOR);
    		this.circumference = this.progressIndicator.r.baseVal.value * 2 * Math.PI; // Progress button circumference
    		this.header = false;
    
    		this.maybeHidden();
    		this.updateProgress(); // Init progress on page load / page refresh
    	}
    
    	bind() {
    		viewport_service.on( 'change', this.viewportChange.bind(this) );
    
    		this.resizeHanlder = throttle(this.updateProgress, 500).bind(this);
    		this.scrollHanlder = throttle(this.updateProgress, 100).bind(this);
    		this.hiddenHanlder = throttle(this.toggleHidden, 100).bind(this);
    
    		this.viewportChange();
    	}
    
    	bindProgress() {
    		window.addEventListener( 'resize', this.resizeHanlder );
    		window.addEventListener( 'scroll', this.scrollHanlder );
    		if ( this.header ) window.addEventListener( 'scroll', this.hiddenHanlder );
    	}
    
    	unbindProgress() {
    
    		window.removeEventListener('resize', this.resizeHanlder );
    		window.removeEventListener('scroll', this.scrollHanlder );
    		if ( this.header ) window.removeEventListener('scroll', this.hiddenHanlder );
    	}
    
    	viewportChange() {
    		this.maybeHidden();
    
    		if ( viewport_service.isMobile() ) {
    			this.bindProgress();
    		}
    		else {
    
    			this.unbindProgress();
    		}
    	}
    
    	updateProgress() {
    		this.relativeScroll = (window.scrollY - this.article.offsetTop) / (this.article.clientHeight - window.innerHeight);
    		this.progress = (
    			(this.relativeScroll < 0) ? 0 : (
    				(this.relativeScroll > 1) ? 1 : this.relativeScroll
    			)
    		);
    		this.progressIndicator.style.strokeDashoffset = this.circumference * (1 - (this.progress / 1));
    	}
    
    	toggleHidden() {
    
    		if( (this.header.getBoundingClientRect().top - parseInt(getComputedStyle(this.header).top)) <= 1 ) {
    			this.removeClass( HIDDEN_STATE );
    		}
    		else {
    			this.addClass( HIDDEN_STATE );
    		}
    	}
    
    	maybeHidden() {
    		if ( !document.body.classList.contains('ads') ) {
    			return;
    		}
    
    		// Hide the button by default
    		this.addClass( HIDDEN_STATE );
    
    		// Get the header element reference to check the sticky state
    		this.header = document.querySelector('.o-header');
    	}
    
    	destroy() {
    		super.destroy();
    
    		this.unbindProgress();
    	}
    }
    
  • URL: /components/raw/a-progress-button/a-progress-button.js
  • Filesystem Path: src/components/atoms/a-progress-button/a-progress-button.js
  • Size: 2.5 KB
{
  "buttonUrl": "#"
}

Only visible on mobile

Integration

To make this button work, assign HTML attribute data-article-progress to the corresponding section / article.

Example <div class="news-article" data-article-progress>