mixin year(value)
	li
		span #{value}

mixin item(year)
	h2.milestone #{year}
	p По сути, стратегия позиционирования определяет рекламный макет. Маркетинговая активность отражает медиаплан, не считаясь с затратами. Общество потребления переворачивает ролевой поведенческий таргетинг.
	p Рекламный блок, суммируя приведенные примеры, развивает межличностный отраслевой стандарт. Надо сказать, что бюджет на размещение изменяет из ряда вон выходящий креатив, отвоевывая рыночный сегмент. Баланс спроса и предложения интегрирована.
	p Дело в том, что креатив ускоряет бренд. Бренд, на первый взгляд, многопланово создает имидж. Узнавание бренда, конечно, разнородно отталкивает институциональный клиентский спрос. Стимулирование сбыта, не меняя концепции, изложенной выше, спорадически уравновешивает фирменный рейтинг, повышая конкуренцию. Концепция новой стратегии оправдывает BTL.


article.timeline
	nav.timeline__nav
		ul
			+year('1993')
			+year('1994')
			+year('1995')
			+year('1996')
			+year('1997')
			+year('1998')
			+year('1999')
			+year('2000')
			+year('2001')
			+year('2002')
			+year('2003')
			+year('2004')
			+year('2005')
			+year('2006')
			+year('2007')
			+year('2008')
			+year('2009')
			+year('2010')
			+year('2011')
			+year('2012')
			+year('2013')
			+year('2014')
			+year('2015')
			+year('2016')
			+year('2017')
			+year('2018')
	section.timeline__section
		.wrapper
			+item('1993')
			+item('1994')
			+item('1995')
			+item('1996')
			+item('1997')
			+item('1998')
			+item('1999')
			+item('2000')
			+item('2001')
			+item('2002')
			+item('2003')
			+item('2004')
			+item('2005')
			+item('2006')
			+item('2007')
			+item('2008')
			+item('2009')
			+item('2010')
			+item('2011')
			+item('2012')
			+item('2013')
			+item('2014')
			+item('2015')
			+item('2016')
			+item('2017')
			+item('2018')
View Compiled
@import url('https://fonts.googleapis.com/css?family=Exo+2:400,700&subset=cyrillic');

html {
	box-sizing: border-box;
}

*,
*::before,
*::after {
	box-sizing: inherit;
}

body {
	font-family: 'Exo 2', sans-serif;
	line-height: 1.5;
}

.wrapper {
	margin: 0 auto;
	padding: 0 16.66% 50px;
	width: 100%;
}

article {
	position: relative;
	max-width: 980px;
	margin: 0 auto;
}

.timeline {
	&__nav {
		position: fixed;
		z-index: 99;
		top: 0;
		transition: top .3s ease-out;

		ul {
			list-style: none;
			list-style-position: inside;
			margin: 15px 0;

			li {
				margin: 15px 0;
				padding-left: 0;
				list-style-type: none;
				color: #bfc1c3;
				border-bottom: 1px dotted rgba(0, 0, 0, 0.3);
				cursor: pointer;
				transition: all .3s ease-out;
				
				&.active {
					font-weight: bold;
					color: #f94125;
					border-bottom: 1px dotted transparent;
					transform: scale(1.2);
				}
				
				&:hover {
					color: #000
				}
			}
		}
	}
}
View Compiled
$(() => {
	let stickyTop = 0,
		scrollTarget = false
	
	let timeline = $('.timeline__nav'),
		items = $('li', timeline),
		milestones = $('.timeline__section .milestone'),
		offsetTop = parseInt(timeline.css('top'))

	const TIMELINE_VALUES = {
		start: 190,
		step: 30
	}

	$(window).resize(function () {
		timeline.removeClass('fixed')

		stickyTop = timeline.offset().top - offsetTop

		$(window).trigger('scroll')
	}).trigger('resize')

	$(window).scroll(function () {
		if ($(window).scrollTop() > stickyTop) {
			timeline.addClass('fixed')
		} else {
			timeline.removeClass('fixed')
		}
	}).trigger('scroll')

	items.find('span').click(function () {
		let li = $(this).parent(),
			index = li.index(),
			milestone = milestones.eq(index)

		if (! li.hasClass('active') && milestone.length) {
			scrollTarget = index

			let scrollTargetTop = milestone.offset().top - 80

			$('html, body').animate({ scrollTop: scrollTargetTop }, {
				duration: 400,
				complete: function complete() {
					scrollTarget = false
				}
			})
		}
	})

	$(window).scroll(function () {
		let viewLine = $(window).scrollTop() + $(window).height() / 3,
			active = -1

		if (scrollTarget === false) {
			milestones.each(function () {
				if ($(this).offset().top - viewLine > 0) {
					return false
				}
				
				active++
			})
		} else {
			active = scrollTarget
		}

		timeline.css('top', -1 * active * TIMELINE_VALUES.step + TIMELINE_VALUES.start + 'px')

		items.filter('.active').removeClass('active')
		
		items.eq(active != -1 ? active : 0).addClass('active')
	}).trigger('scroll')
})
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js