"Twitter Responsive Tabview"
Bootstrap 3.3.0 Snippet by harryom

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <div class="title"> 🐦 <br/> Twitter Responsive Tabs </div> <div class="view"> <div class="bio"> <div class="bio--header"> <div class="bio--header-image-wrapper"> <img class="bio--header-image" src="https://pbs.twimg.com/profile_images/780306197610106880/YR1otDu7_400x400.jpg" alt="Lubos' picture" /> </div> <div class="bio--header-buttons"> <a href="mailto:lmenus@lmen.us" title="Send me an email">Email</a> <a href="https://twitter.com/lmenus" target="_blank" title="Go to my Twitter profile">Follow</a> </div> </div> <h2>Lubos</h2> <h3>@lmenus</h3> <p>Chief Idea Officer. Blog <a href="https://bold.io/@lmenus" target="_blank">bold.io/@lmenus</a></p> </div> <div class="tabbar"> <ul class="tabbar--list"> <li class="tabbar--item">Tweets</li> <li class="tabbar--item">Tweets et réponses</li> <li class="tabbar--item">Médias</li> <li class="tabbar--item">J'aime</li> </ul> <div class="tabbar--border"><span></span></div> </div> <div class="feed"> <h3>«<span class="js-view-name"></span>»</h3> <p>Sweeet tab view animation 🍬</p> </div> </div> <div class="footer">© 2017 Lubos</div>
$c-bg-bio: #1b2836 !default; $c-bg-body: #eee !default; $c-bg-feed: #141d26 !default; $c-bg-tabbar: #243447 !default; $c-text-body: #555 !default; $c-text-feed-secondary: #8899a6 !default; $c-text-tabbar: #8899a6 !default; $c-text-tabbar-active: #1da1f2 !default; $s-bio-border: 8px !default; $s-bio-image: 120px; $s-border-h: .2rem !default; $border-radius: 8px; $q: 640px !default; *, *:after, *:before { box-sizing: border-box; -webkit-tap-highlight-color: rgba(#000, 0); } body { align-items: center; background-color: $c-bg-body; color: $c-text-body; display: flex; flex-direction: column; font-family: 'Dancing Script', cursive; justify-content: center; min-height: 100vh; } a { color: inherit; font-weight: 700; } .bio { background-color: $c-bg-bio; color: #fff; min-height: 10rem; padding: 1rem; padding-top: $s-bio-image * .6; position: relative; width: 100%; > .bio--header { $margin: 1rem; align-items: flex-end; display: flex; justify-content: space-between; left: $margin; min-width: calc(100% - #{$margin * 2}); position: absolute; top: -$s-bio-image * .4; > .bio--header-image-wrapper { background-color: $c-bg-bio; border: $s-bio-border solid $c-bg-bio; border-radius: $border-radius; height: $s-bio-image; overflow: hidden; width: $s-bio-image; > .bio--header-image { border-radius: $border-radius; display: block; height: 100%; width: 100%; } } > .bio--header-buttons { display: flex; position: relative; top: -$s-bio-border; > a { border: 2px solid currentColor; border-radius: 4px; color: $c-text-tabbar; font-size: 1.4em; font-weight: 600; margin-left: 8px; padding: 8px 1em; text-decoration: none; transition: border-color .2s, color .2s; &:hover { color: $c-text-tabbar-active; } } } } > h2 { font-size: 1.8rem; font-weight: 600; margin: 1rem auto .4rem; } > h3 { color: $c-text-feed-secondary; font-size: 1.3rem; font-weight: 600; margin: .4rem auto; } > p { color: #fff; font-size: 1.4rem; font-weight: 500; line-height: 1.4; margin: 1rem auto; > a { color: $c-text-tabbar-active; font-weight: inherit; text-decoration: none; &:hover { text-decoration: underline; } } } } .feed { background-color: $c-bg-feed; color: #fff; min-height: 30rem; padding: 3rem; text-align: center; width: 100%; > h3 { font-size: 1.8rem; font-weight: 600; line-height: 1.2; margin: 1.2rem auto; } > p { color: $c-text-feed-secondary; font-size: 1.2rem; font-weight: 600; margin: 1.2rem auto; } } .footer { font-size: 2em; line-height: 1.6; text-align: center; } .tabbar { backface-visibility: hidden; display: block; margin: 0 auto; max-width: 100%; position: relative; transform: translateZ(0) scale(1); > .tabbar--list { border-radius: 2px; display: flex; > .tabbar--item { align-items: center; background-color: $c-bg-tabbar; border-bottom: $s-border-h solid transparent; color: $c-text-tabbar; cursor: pointer; display: flex; font-size: 1.2em; font-weight: 700; height: 2em; justify-content: center; padding: 1.6em 1.2em; text-transform: uppercase; white-space: nowrap; &:hover, &.active { color: $c-text-tabbar-active; } } } > .tabbar--border { background-color: $c-text-tabbar-active; bottom: 0; display: block; height: $s-border-h; left: 0; position: absolute; width: 0; > span { background-color: $c-text-tabbar-active; display: block; height: 100%; transform-origin: 0 50%; width: 0; } } } .title { font-size: 4em; line-height: 1.2; text-align: center; } .view { box-shadow: 15px 15px 3px rgba(#000, .3); font-family: 'Open Sans', sans-serif; margin: 5rem auto 1.6rem; max-width: 100%; @media screen and (min-width: $q) { max-width: $q; } }
const TABBAR_BORDER_TRANSFORM_EASING = 'ease' const TABBAR_BORDER_TRANSFORM_SPEED = 400 const TABBAR_ITEM_ACTIVE_CLASSNAME = 'active' let _ActiveTabBarItemNode = null let _ActiveTabBarItemIndex = -1 let _TabBarBorderNode = null let _TabBarListNode = null let _FeedViewName = null const addTabBarItemProps = () => { const items = _TabBarListNode.children for (let i = 0; i < items.length; i++) { const item = items[i] item.style.transition = `color ${TABBAR_BORDER_TRANSFORM_SPEED}ms ${TABBAR_BORDER_TRANSFORM_EASING}` item.addEventListener('click', () => setTabBarItemActive(i) ) } } const getNodeCenter = clientRect => { return (clientRect.left + clientRect.right) / 2 } const getTabBarItem = index => { return index < _TabBarListNode.children.length ? _TabBarListNode.children[index] : false } const initTabBar = index => { const node = document.getElementsByClassName('tabbar')[0] const feedName = document.getElementsByClassName('js-view-name')[0] if (_TabBarListNode || _TabBarBorderNode || !node) return if (feedName) _FeedViewName = feedName _TabBarListNode = node.children[0] _TabBarBorderNode = node.children[1] // This way, we can use integer numbers for scale transform. _TabBarBorderNode.children[0].style.width = '1px' // Wait until the border has applied styles. setTimeout( () => { addTabBarItemProps() // Do not animate the first state transition. setTabBarItemActive(index).then( () => { setTimeout( () => { _TabBarBorderNode.style.transition = `transform ${TABBAR_BORDER_TRANSFORM_SPEED}ms ${TABBAR_BORDER_TRANSFORM_EASING}` _TabBarBorderNode.children[0].style.transition = `transform ${TABBAR_BORDER_TRANSFORM_SPEED}ms ${TABBAR_BORDER_TRANSFORM_EASING}` window.addEventListener('resize', () => setTabBarItemActive(_ActiveTabBarItemIndex, true) ) }, 0) }) }, 100) } const moveTabBarBorder = (index, noAnimation) => { return new Promise( (resolve, reject) => { let activeItemClientRect = _ActiveTabBarItemNode.getBoundingClientRect() let borderClientRect = _TabBarBorderNode.getBoundingClientRect() let firstItemClientRect = getTabBarItem(0).getBoundingClientRect() let tempTabBarBorderNodeStyle, tempTabBarBorderSpanStyle activeItemClientRect.centerX = getNodeCenter(activeItemClientRect) borderClientRect.centerX = getNodeCenter(borderClientRect) const currentTransformTranslateX = borderClientRect.left - (firstItemClientRect.left < 0 ? 0 : firstItemClientRect.left) const newTransformTranslateX = currentTransformTranslateX + (activeItemClientRect.centerX - borderClientRect.centerX) if (noAnimation) { tempTabBarBorderNodeStyle = _TabBarBorderNode.style.transition tempTabBarBorderSpanStyle = _TabBarBorderNode.children[0].style.transition _TabBarBorderNode.style.transition = '' _TabBarBorderNode.children[0].style.transition = '' } setTimeout( () => { _TabBarBorderNode.style.transform = `translateX(${newTransformTranslateX}px)` _TabBarBorderNode.children[0].style.transform = `scaleX(${activeItemClientRect.width}) translateX(-50%)` if (noAnimation) { setTimeout( () => { if (tempTabBarBorderNodeStyle) { _TabBarBorderNode.style.transition = tempTabBarBorderNodeStyle } if (tempTabBarBorderSpanStyle) { _TabBarBorderNode.children[0].style.transition = tempTabBarBorderSpanStyle } return resolve() }, 0) } return resolve() }, 0) }) } const setTabBarItemActive = (index, isEventListener) => { return new Promise( (resolve, reject) => { if (index === -1) return resolve() // Event listeners do not change the index. if (isEventListener) moveTabBarBorder(index, isEventListener).then( () => resolve() ) if (_ActiveTabBarItemNode) { _ActiveTabBarItemNode.classList.remove(TABBAR_ITEM_ACTIVE_CLASSNAME) } _ActiveTabBarItemIndex = index _ActiveTabBarItemNode = getTabBarItem(index) _ActiveTabBarItemNode.classList.add(TABBAR_ITEM_ACTIVE_CLASSNAME) _FeedViewName.innerHTML = _ActiveTabBarItemNode.innerHTML moveTabBarBorder(index).then( () => resolve() ) }) } window.addEventListener('load', () => initTabBar(0) )

Related: See More


Questions / Comments: