Features
Using Vue Horizontal
You can display horizontal content like you would do with just any other HTML content vertically.
Naturally, that would allow you to use v-for
for a consistent design template.
- Any HTML structure works, you can mix and match them up.
- Use
v-for
and/orv-if
with anydiv
orcomponent
- Navigation left, right button will automatically appear if there are any items overflowing
- Trackpad and touch scrolling will work as expected.
<template>
<vue-horizontal>
<div class="item">
<h3>HTML Tag</h3>
<p>As you can see these are just html elements.</p>
</div>
<section>
<h4>Don't have to be the same tag</h4>
<p>I used a h4 instead of a h3</p>
</section>
<section>
<h3>Navigation Button</h3>
<p>The navigation button will appear if there is an overflow.</p>
</section>
<section>
<h3>Scroll</h3>
<p>You can just trackpad to scroll still!</p>
</section>
<section>
<h3>Touch screen</h3>
<p>Touch screen works too!</p>
</section>
<section v-for="item in items" :key="item.i">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
</section>
<section>
<h3>Last item?</h3>
<p>Maybe you want to display something different at the end?</p>
</section>
</vue-horizontal>
</template>
<script>
export default {
data() {
return {
// E.g: creates 5 array items...
items: [...Array(5).keys()].map((i) => {
return {i, title: `v-for: ${i}`, content: `🚀 Paragraph ${i}`};
}),
}
}
}
</script>
<style scoped>
section,
.item {
background: #f3f3f3;
padding: 16px 24px;
margin-right: 24px;
}
</style>
Responsive
It comes with default set of responsive breakpoints that is disabled by default.
We believe that CSS should be written by the users and not controlled by a library.
However, for the sake of convenience, you can use enable it with the responsive
prop.
Check out responsive 101 for a detailed write-up about responsive horizontal design.
Breakpoints
They are created in CSS with media queries, the viewport of the screen (your browser actual width).
< 640px
1 item in container< 768px
2 item in container< 1024px
3 item in container< 1280px
4 item in container> 1280px
5 item in container
<template>
<vue-horizontal responsive>
<section v-for="item in items" :key="item.i">
<h4>{{ item.title }}</h4>
<p>{{ item.content }}</p>
</section>
</vue-horizontal>
</template>
<script>
export default {
data() {
return {
// E.g: creates 20 array items...
items: [...Array(20).keys()].map((i) => {
return {i, title: `Responsive`, content: `Content`};
}),
}
}
}
</script>
<style scoped>
section {
padding: 16px 24px;
background: #f3f3f3;
}
</style>
Scroll snapping
<vue-horizontal snap="start|center|end">
- start (default)
- center
- end
- none to turn it off
<vue-horizontal snap="start">
<template>
<vue-horizontal snap="start">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Start {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
<vue-horizontal snap="center">
<template>
<vue-horizontal snap="center">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Center {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
<vue-horizontal snap="end">
<template>
<vue-horizontal snap="end">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>End {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
<vue-horizontal snap="none">
<template>
<vue-horizontal snap="none">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Center {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
Scroll bar
The Scroll bar is disabled by default, you can enable it the scroll
prop.
<vue-horizontal scroll>
<template>
<vue-horizontal scroll>
<component-example class="component" v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Scroll bar {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
<style scoped>
.component {
margin-bottom: 24px;
}
</style>
Navigation button
- Due to the varying nature of margins, padding, box-sizing and content-sizing, next/prev are done on best effort basis.
- Default implementation should work for 95% of the use cases.
- Alternatively you can use
scrollToIndex
ofscrollToLeft
.
- Snapping might not work as expected if smoothscroll is polyfill-ed.
<vue-horizontal :button="false">
<template>
<vue-horizontal :button="false">
<component-example class="component" v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Disabled {{i}}</h3>
</component-example>
</vue-horizontal>
</template>
Override with slots
<template>
<vue-horizontal>
<component-example class="component" v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Override {{i}}</h3>
</component-example>
<template v-slot:btn-prev>
<button>Prev</button>
</template>
<button slot="btn-next">
Next
</button>
</vue-horizontal>
</template>
<style scoped>
button {
padding: 8px;
background: black;
color: white;
font-weight: 700;
}
</style>
Events
@prev @next
Emitted when prev or next are clicked.
<template>
<div>
<vue-horizontal @prev="onPrev" @next="onNext">
<component-example v-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>Event {{ i }}</h3>
</component-example>
</vue-horizontal>
<pre>{{prev}}</pre>
<pre>{{next}}</pre>
</div>
</template>
<script>
export default {
data() {
return {
prev: 'no-event',
next: 'no-event',
}
},
methods: {
onPrev() {
this.prev += '- clicked prev'
},
onNext() {
this.next += '- clicked next'
}
}
}
</script>
@scroll
@scroll-debounce is called when mounted.
<template>
<div>
<vue-horizontal @scroll="onScroll" @scroll-debounce="onScrollDebounce">
<component-example v-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>Event {{ i }}</h3>
</component-example>
</vue-horizontal>
<pre>{{scroll}}</pre>
<pre>{{scrollDebounce}}</pre>
</div>
</template>
<script>
export default {
data() {
return {
scroll: 'no-event',
scrollDebounce: 'no-event',
}
},
methods: {
onScroll(data) {
this.scroll = data
},
onScrollDebounce(data) {
this.scrollDebounce = data
}
}
}
</script>
Methods
prev()
next()
- Scroll to the next/prev set of elements.
- Elements that are half visible, will not be scrolled past.
- Due to the varying nature of margins, padding, box-sizing and content-sizing, next/prev are done on best effort basis.
- Default implementation should work for 95% of the use cases.
- Alternatively you can use
scrollToIndex
ofscrollToLeft
.
- Snapping might not work as expected if smoothscroll is polyfill-ed.
<template>
<div>
<vue-horizontal ref="horizontal">
<component-example v-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>Event {{ i }}</h3>
</component-example>
</vue-horizontal>
<button @click="$refs.horizontal.prev()">Prev</button>
<button @click="$refs.horizontal.next()">Next</button>
</div>
</template>
<style scoped>
button {
padding: 4px 16px;
border-radius: 3px;
background: black;
color: white;
font-weight: 700;
margin: 24px 12px 0 0;
}
</style>
scrollToIndex()
- Scroll to the index of the elements in
vue-horizontal
.
<template>
<div>
<vue-horizontal ref="horizontal">
<component-example v-for="i in [0,1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>Event {{ i }}</h3>
</component-example>
</vue-horizontal>
<button v-for="i in [0,1,2,3,4]" @click="$refs.horizontal.scrollToIndex(i)">
{{i}}
</button>
</div>
</template>
<style scoped>
button {
padding: 4px 16px;
border-radius: 3px;
background: black;
color: white;
font-weight: 700;
margin: 24px 12px 0 0;
}
</style>
scrollToLeft()
- The amount of pixel to the left you want to scroll by.
- Snap settings
snap="start|end|center"
will prevent the scrolling if it snaps backwards. - Scroll behavior options
scrollToLeft(100)
default to 'smooth'scrollToLeft(100, 'smooth')
smooth scrollscrollToLeft(100, 'auto')
without smooth scroll
<template>
<div>
<vue-horizontal ref="horizontal">
<component-example v-for="i in [0,1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>Event {{ i }}</h3>
</component-example>
</vue-horizontal>
<button v-for="i in [0,100,150,300,1000,3000]" @click="$refs.horizontal.scrollToLeft(i)">
{{i}}
</button>
</div>
</template>
<style scoped>
button {
padding: 4px 16px;
border-radius: 3px;
background: black;
color: white;
font-weight: 700;
margin: 24px 12px 0 0;
}
</style>
Displacement
Displacement is a positive float number that you can override for a custom scroll displacement. Defaults to 1.0
.
Changing the value affects next/prev button and .next()
.prev()
function call as well.
<template>
<div>
<vue-horizontal :displacement="0.5">
<component-example v-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
<h3>50% {{ i }}</h3>
</component-example>
</vue-horizontal>
<vue-horizontal :displacement="1.75" style="margin-top: 24px">
<component-example v-for="i in [1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18,19]" :key="i">
<h3>175% {{ i }}</h3>
</component-example>
</vue-horizontal>
</div>
</template>
CSS
Overriding internal CCS with Vue deep selector (>>>
).
.v-hl-btn
.v-hl-btn
for both left and right button..v-hl-btn-prev
.v-hl-btn-next
<template>
<vue-horizontal class="this">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Button {{ i }}</h3>
</component-example>
</vue-horizontal>
</template>
<style scoped>
.this >>> .v-hl-btn {
filter: invert(1);
}
.this >>> .v-hl-btn-next {
transform: translateX(0);
}
.this >>> .v-hl-btn-prev {
top: 0;
}
.this >>> .v-hl-btn-prev svg {
margin: 0;
padding: 4px;
height: 30px;
width: 30px;
}
</style>
.v-hl-container
<template>
<vue-horizontal class="this">
<component-example v-for="i in [1,2,3,4,5,6,7,8]" :key="i">
<h3>Button {{ i }}</h3>
</component-example>
</vue-horizontal>
</template>
<style scoped>
.this >>> .v-hl-container {
background: black;
}
</style>
.vue-horizontal
The root div, has .vue-horizontal
as the only class with 2 CSS attributes.
Ensure you don't override them as they are used for nav button positioning.
position: relative;
display: flex;