<button type="button" class="button">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
}
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<a href="https://play.ee/" class="button">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</a>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text",
"link": "https://play.ee/"
}
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--icon">
<span class="button__inner">
<span class="button__icon-wrapper"><svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#search-24"></use>
</svg>
</span> <span class="button__text">Search</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Search",
"icon": "search-24"
}
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--icon-left">
<span class="button__inner">
<span class="button__icon-wrapper"><svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#plus-24"></use>
</svg>
</span> <span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text",
"icon": "plus-24",
"iconPosition": "left"
}
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--icon-right">
<span class="button__inner">
<span class="button__icon-wrapper"><svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#plus-24"></use>
</svg>
</span> <span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text",
"icon": "plus-24",
"iconPosition": "right"
}
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--arrow-animate button--icon-right">
<span class="button__inner">
<span class="button__icon-wrapper"><svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</span> <span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text",
"icon": "tiny-arrow-right-long-24",
"iconPosition": "right"
},
"modifier": "button--arrow-animate"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
Modifier to force button always full width.
<button type="button" class="button button--block">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
},
"modifier": "button--block"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
Modifier to force button full width only in mobile.
<button type="button" class="button button--block-xs">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
},
"modifier": "button--block-xs"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
e.g explore button with 45deg rotating arrow icon
<button type="button" class="button button--arrow-rotate button--icon-right">
<span class="button__inner">
<span class="button__icon-wrapper"><svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-right-top-24"></use>
</svg>
</span> <span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text",
"icon": "tiny-right-top-24",
"iconPosition": "right"
},
"modifier": "button--arrow-rotate"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--text">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
},
"modifier": "button--text"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--reverted">
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
},
"modifier": "button--reverted"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}
<button type="button" class="button button--magnetic">
<span class="button__filler"></span>
<span class="button__inner">
<span class="button__text">Button text</span>
</span>
</button>
{% set element = data.link ? 'a' : 'button' %}
{% set icon = icon|default(data.icon|default(false)) %}
{% set iconPosition = iconPosition|default(data.iconPosition|default(false)) %}
{% set BEM -%}
button
{%- if modifier %} {{ modifier }}{% endif %}
{%- if class %} {{ class }}{% endif %}
{%- if icon %} button--icon{% if iconPosition %}{{ '-' ~ iconPosition }}{% endif %}{% endif %}
{% endset %}
<{{ element }}
{% if data.link %} href="{{ data.link }}"{% else %} type="{{ type|default('button') }}" {% endif %}
class="{{ BEM }}"
{%- if data.attributes %} {{ data.attributes }}{% endif %}
>
{% if 'button--magnetic' in modifier or 'button--magnetic' in class %}
<span class="button__filler"></span>
{% endif %}
<span class="button__inner">
{% if data.icon %}<span class="button__icon-wrapper">{% include '@icon' with { name: data.icon, modifier: '', class: 'button__icon' } %}</span>{% endif %}
<span class="button__text">{{ data.text }}</span>
</span>
</{{ element }}>
{
"language": "en-US",
"data": {
"text": "Button text"
},
"modifier": "button--magnetic"
}
$button-primary-background: $color-brand-01;
$button-primary-hover: $color-text-03;
$button-primary-active: $color-brand-01;
.button {
position: relative;
border-radius: $border-radius-base;
border: none;
-webkit-appearance: none;
text-align: center;
cursor: pointer;
min-width: 156px;
display: inline-block;
padding: 14px 32px;
margin: 0;
font-size: $font-size-small;
font-weight: $font-weight-medium;
line-height: 1.15;
background-color: transparent;
color: $button-primary-hover;
text-decoration: none;
text-transform: uppercase;
transition-property: color;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
z-index: map-get($zindex, 'default');
outline-offset: 2px;
overflow: hidden;
.text > * + & {
margin-top: 1.5em;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $button-primary-background;
transition-property: background-color, transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
box-shadow: inset 0 0 0 2px $button-primary-background;
transform: scale(1);
border-radius: $border-radius-base;
z-index: 4;
}
&.is-hover,
&:hover {
transition-timing-function: $transition-easing-out;
color: $button-primary-background;
&:before {
transition-timing-function: $transition-easing-out;
background-color: transparent;
}
}
&:active {
color: $button-primary-background;
&:before {
background-color: transparent;
transform: scale(.96);
transition: none;
}
}
}
.button--icon {
min-width: 0;
padding: 12px;
border-radius: $border-radius-round;
&:before {
border-radius: $border-radius-round;
}
}
.button--icon-left {
padding-left: 56px;
}
.button--icon-right {
padding-right: 56px;
}
.button--arrow-animate {
padding-right: 48px;
}
.button--reverted {
color: $button-primary-background;
&:before {
background-color: $button-primary-hover;
box-shadow: inset 0 0 0 2px $button-primary-hover;
}
&.is-hover,
&:hover {
color: $button-primary-hover;
&:before {
background-color: $button-primary-background;
}
}
&:active {
color: $button-primary-hover;
}
}
.button--magnetic {
html[data-whatintent='mouse'] & {
background-color: $button-primary-background;
}
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
&.is-hover,
&:active,
&:hover {
&:before {
html[data-whatintent='mouse'] & {
background-color: transparent;
}
}
}
&.button--reverted {
html[data-whatintent='mouse'] & {
background-color: $button-primary-hover;
}
}
}
.button--block {
display: block;
width: 100%;
}
.button--block-xs {
@media only screen and (max-width: $bp-sm-min - 1) {
display: block;
width: 100%;
}
}
.button__icon-wrapper {
position: relative;
z-index: 5;
.button--icon & {
display: block;
}
.button--icon-left &,
.button--icon-right & {
position: absolute;
top: auto;
bottom: auto;
margin: auto;
display: inline-block;
flex: 0 0 24px;
}
.button--icon-left & {
left: 32px;
}
.button--icon-right & {
right: 32px;
}
.button--arrow-animate & {
width: 16px;
height: 24px;
overflow: hidden;
right: 32px;
transition-property: width, right;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
.button--arrow-animate.is-hover &,
.button--arrow-animate:hover & {
width: 24px;
right: 24px;
transition-timing-function: $transition-easing-out;
}
.button--arrow-rotate & {
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
transform: rotate(0);
}
.button--arrow-rotate.is-hover &,
.button--arrow-rotate:hover & {
transition-timing-function: $transition-easing-out;
transform: rotate(45deg);
}
}
.button__icon {
font-size: 24px;
display: block;
.button--arrow-animate & {
float: right;
}
}
.button__text {
position: relative;
z-index: 5;
.button--icon & {
@include visually-hidden();
}
.button--icon-left & {
margin-left: 8px;
}
.button--icon-right & {
margin-right: 8px;
}
}
.button__inner {
display: flex;
align-items: center;
justify-content: center;
}
.button--text {
color: $button-primary-hover;
position: relative;
transition: $transition-duration $transition-easing;
min-width: auto;
padding: 0;
text-transform: none;
&:hover {
color: $button-primary-hover;
}
&:before {
content: '';
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: -1px;
width: 100%;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
&:hover:before {
width: 0;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 0;
border-bottom: 2px solid $button-primary-hover;
transition: width $transition-duration $transition-easing-in, border-bottom $transition-duration $transition-easing-in;
}
&:hover:after {
width: 100%;
transition: width $transition-duration $transition-easing-out $transition-duration, border-bottom $transition-duration $transition-easing-out;
}
}
.button__filler {
display: none;
position: absolute;
z-index: 2;
width: 150%;
height: 200%;
border-radius: $border-radius-round;
background-color: $color-ui-01;
color: $color-text-01;
top: -50%;
left: -25%;
transform: translate3d(0, 75%, 0);
transition-property: transform;
transition-duration: $transition-duration;
transition-timing-function: $transition-easing-in;
will-change: transform; /* stylelint-disable-line plugin/no-unsupported-browser-features */
.button--magnetic & {
html[data-whatintent='mouse'] & {
display: block;
}
}
.button--reverted & {
background-color: $button-primary-background;
color: $color-text-03;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import Icon from '../icon/icon';
import './button.scss';
export interface IButton {
text: string;
icon?: string;
link?: string;
iconPosition?: string;
}
export interface ITransform {
amt: number;
current: number;
previous: number;
}
export interface IStyles {
tx: ITransform;
ty: ITransform;
}
export default class Button extends Component {
static initSelector: string = '.button--magnetic';
public filler: JQuery;
public renderedStyles: IStyles;
public buttonRect: DOMRect;
public distanceToTrigger: number;
constructor(element: HTMLElement) {
super(element);
this.filler = this.element.find('.button__filler');
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
this.renderedStyles = {
tx: {amt: 0.1, current: 0, previous: 0},
ty: {amt: 0.1, current: 0, previous: 0},
};
this.init();
}
static render(data: IButton, className?: string): JQuery {
const element: string = data.link ? 'a' : 'button';
const icon: string = data.icon ? Icon.render(data.icon, 'button__icon') : '';
const classArray: string[] = ['button'];
if (className) {
classArray.push(className);
}
if (data.icon && !data.iconPosition) {
classArray.push('button--icon');
}
if (data.icon && data.iconPosition === 'right') {
classArray.push('button--icon-right');
}
if (data.icon && data.iconPosition === 'left') {
classArray.push('button--icon-left');
}
return $(`<${element} class="${classArray.join(' ')}" ${data.link ? `href="${data.link}"` : 'type="button"'}><span class="button__filler"></span><span class="button__inner"><span class="button__icon-wrapper">${icon}</span><span class="button__text">${data.text}</span></span></${element}>`);
}
init(): void {
if (!Helpers.isMobileDevice) {
this.element.on('mouseenter', this.enter.bind(this));
this.element.on('mouseleave', this.leave.bind(this));
$(window).on('resize scroll', this.resizeHandler.bind(this));
this.run();
}
}
resizeHandler(): void {
this.buttonRect = this.element[0].getBoundingClientRect();
this.distanceToTrigger = this.buttonRect.width * 0.7;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
const a: number = x1 - x2;
const b: number = y1 - y2;
return Math.hypot(a,b);
}
lerp(a: number, b: number, n: number): number {
return (1 - n) * a + n * b;
}
run(): void {
// Linear interpolation
const distanceMouseButton: number = this.distance(window.gotoAndPlay.mousePos.x, window.gotoAndPlay.mousePos.y, this.buttonRect.left + this.buttonRect.width / 2, window.scrollY + this.buttonRect.top + this.buttonRect.height / 2);
// new values for the translations
let x: number = 0;
let y: number = 0;
if (distanceMouseButton < this.distanceToTrigger) {
x = (window.gotoAndPlay.mousePos.x - (this.buttonRect.left + this.buttonRect.width / 2)) * .3;
y = (window.gotoAndPlay.mousePos.y - (window.scrollY + this.buttonRect.top + this.buttonRect.height / 2)) * .3;
}
this.renderedStyles.tx.current = parseFloat(x.toFixed(3));
this.renderedStyles.ty.current = parseFloat(y.toFixed(3));
for (const key in this.renderedStyles) {
if (key) {
this.renderedStyles[key].previous = parseFloat(this.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt).toFixed(3));
}
}
this.element[0].style.transform = `translate3d(${this.renderedStyles.tx.previous}px, ${this.renderedStyles.ty.previous}px, 0)`;
requestAnimationFrame(() => this.run());
}
enter(): void {
this.element.addClass('is-hover');
this.filler[0].style.transitionDuration = `250ms`;
this.filler[0].style.transform = `translate3d(0, 0, 0)`;
}
leave(): void {
this.element.removeClass('is-hover');
this.filler[0].style.transform = `translate3d(0, -75%, 0)`;
setTimeout(() => {
if (!this.element.hasClass('is-hover')) {
this.filler[0].style.transitionDuration = `0ms`;
this.filler[0].style.transform = `translate3d(0, 75%, 0)`;
}
}, 250);
}
}