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/or v-if with any div or component
  • Navigation left, right button will automatically appear if there are any items overflowing
  • Trackpad and touch scrolling will work as expected.

HTML Tag

As you can see these are just html elements.

Don't have to be the same tag

I used a h4 instead of a h3

Navigation Button

The navigation button will appear if there is an overflow.

Scroll

You can just trackpad to scroll still!

Touch screen

Touch screen works too!

v-for: 0

🚀 Paragraph 0

v-for: 1

🚀 Paragraph 1

v-for: 2

🚀 Paragraph 2

v-for: 3

🚀 Paragraph 3

v-for: 4

🚀 Paragraph 4

Last item?

Maybe you want to display something different at the end?

Component.vueimport=features/features-using.vue
<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

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive

Content

Responsive.vueimport=features/features-responsive.vue
<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">

Start 1

Paragraph Text

Start 2

Paragraph Text

Start 3

Paragraph Text

Start 4

Paragraph Text

Start 5

Paragraph Text

Start 6

Paragraph Text

Start 7

Paragraph Text

Start 8

Paragraph Text

ScrollSnappingStart.vueimport=features/features-scroll-snapping-start.vue
<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">

Center 1

Paragraph Text

Center 2

Paragraph Text

Center 3

Paragraph Text

Center 4

Paragraph Text

Center 5

Paragraph Text

Center 6

Paragraph Text

Center 7

Paragraph Text

Center 8

Paragraph Text

ScrollSnappingCenter.vueimport=features/features-scroll-snapping-center.vue
<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">

End 1

Paragraph Text

End 2

Paragraph Text

End 3

Paragraph Text

End 4

Paragraph Text

End 5

Paragraph Text

End 6

Paragraph Text

End 7

Paragraph Text

End 8

Paragraph Text

ScrollSnappingEnd.vueimport=features/features-scroll-snapping-end.vue
<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">

Center 1

Paragraph Text

Center 2

Paragraph Text

Center 3

Paragraph Text

Center 4

Paragraph Text

Center 5

Paragraph Text

Center 6

Paragraph Text

Center 7

Paragraph Text

Center 8

Paragraph Text

ScrollSnappingNone.vueimport=features/features-scroll-snapping-none.vue
<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>

Scroll bar 1

Paragraph Text

Scroll bar 2

Paragraph Text

Scroll bar 3

Paragraph Text

Scroll bar 4

Paragraph Text

Scroll bar 5

Paragraph Text

Scroll bar 6

Paragraph Text

Scroll bar 7

Paragraph Text

Scroll bar 8

Paragraph Text

Scrollbar.vueimport=features/features-scroll-bar.vue
<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>
  • 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 of scrollToLeft.
  • Snapping might not work as expected if smoothscroll is polyfill-ed.

<vue-horizontal :button="false">

Disabled 1

Paragraph Text

Disabled 2

Paragraph Text

Disabled 3

Paragraph Text

Disabled 4

Paragraph Text

Disabled 5

Paragraph Text

Disabled 6

Paragraph Text

Disabled 7

Paragraph Text

Disabled 8

Paragraph Text

NavButtonDisable.vueimport=features/features-nav-button-disable.vue
<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>

<vue-horizontal :button-between="false">

Disabled 1

Paragraph Text

Disabled 2

Paragraph Text

Disabled 3

Paragraph Text

Disabled 4

Paragraph Text

Disabled 5

Paragraph Text

Disabled 6

Paragraph Text

Disabled 7

Paragraph Text

Disabled 8

Paragraph Text

NavButtonBetween.vueimport=features/features-nav-button-between.vue
<template>
  <vue-horizontal responsive :button-between="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

Override 1

Paragraph Text

Override 2

Paragraph Text

Override 3

Paragraph Text

Override 4

Paragraph Text

Override 5

Paragraph Text

Override 6

Paragraph Text

Override 7

Paragraph Text

Override 8

Paragraph Text

NavButtonSlot.vueimport=features/features-nav-button-slot.vue
<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>

    <template v-slot:btn-next>
      <button>Next</button>
    </template>
  </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.

Event 1

Paragraph Text

Event 2

Paragraph Text

Event 3

Paragraph Text

Event 4

Paragraph Text

Event 5

Paragraph Text

Event 6

Paragraph Text

Event 7

Paragraph Text

Event 8

Paragraph Text

Event 9

Paragraph Text

Event 10

Paragraph Text

Event 11

Paragraph Text

Event 12

Paragraph Text

no-event
no-event
EventNavigation.vueimport=features/features-event-prev-next.vue
<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

For convenience, @scroll-debounce is called when mounted.

Event 1

Paragraph Text

Event 2

Paragraph Text

Event 3

Paragraph Text

Event 4

Paragraph Text

Event 5

Paragraph Text

Event 6

Paragraph Text

Event 7

Paragraph Text

Event 8

Paragraph Text

Event 9

Paragraph Text

Event 10

Paragraph Text

Event 11

Paragraph Text

Event 12

Paragraph Text

no-event
no-event
EventScroll.vueimport=features/features-event-scroll.vue
<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 of scrollToLeft.
  • Snapping might not work as expected if smoothscroll is polyfill-ed.

Event 1

Paragraph Text

Event 2

Paragraph Text

Event 3

Paragraph Text

Event 4

Paragraph Text

Event 5

Paragraph Text

Event 6

Paragraph Text

Event 7

Paragraph Text

Event 8

Paragraph Text

Event 9

Paragraph Text

Event 10

Paragraph Text

Event 11

Paragraph Text

Event 12

Paragraph Text

MethodNavigation.vueimport=features/features-method-prev-next.vue
<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.

Event 0

Paragraph Text

Event 1

Paragraph Text

Event 2

Paragraph Text

Event 3

Paragraph Text

Event 4

Paragraph Text

Event 5

Paragraph Text

Event 6

Paragraph Text

Event 7

Paragraph Text

Event 8

Paragraph Text

Event 9

Paragraph Text

Event 10

Paragraph Text

Event 11

Paragraph Text

Event 12

Paragraph Text

MethodScrollIndex.vueimport=features/features-method-scroll-index.vue
<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 scroll
    • scrollToLeft(100, 'auto') without smooth scroll

Event 0

Paragraph Text

Event 1

Paragraph Text

Event 2

Paragraph Text

Event 3

Paragraph Text

Event 4

Paragraph Text

Event 5

Paragraph Text

Event 6

Paragraph Text

Event 7

Paragraph Text

Event 8

Paragraph Text

Event 9

Paragraph Text

Event 10

Paragraph Text

Event 11

Paragraph Text

Event 12

Paragraph Text

MethodScrollLeft.vueimport=features/features-method-scroll-left.vue
<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>

refresh()

Update Vue Horizontal internal data required for rendering update after dom changes.

Vue Horizontal uses slot that can encompass any HTML passed in without parsing, thus you need to tell Vue Horizontal when you updated the dom. This is designed to prevent cyclical event loop that causes browser to hang.

<template>
  <vue-horizontal ref="horizontal">
    <div v-for="i in items"></div>
  </vue-horizontal>
</template>

<script>
export default {
  data() {
    return {
      items: [0,1,2,3]
    }
  },
  methods: {
    update() {
      this.items.push(4,5,6,7,8)
      this.$refs.horizontal.refresh()
    }
  }
}
</script>

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.

50% 1

Paragraph Text

50% 2

Paragraph Text

50% 3

Paragraph Text

50% 4

Paragraph Text

50% 5

Paragraph Text

50% 6

Paragraph Text

50% 7

Paragraph Text

50% 8

Paragraph Text

50% 9

Paragraph Text

50% 10

Paragraph Text

50% 11

Paragraph Text

50% 12

Paragraph Text

175% 1

Paragraph Text

175% 2

Paragraph Text

175% 3

Paragraph Text

175% 4

Paragraph Text

175% 5

Paragraph Text

175% 6

Paragraph Text

175% 7

Paragraph Text

175% 8

Paragraph Text

175% 9

Paragraph Text

175% 10

Paragraph Text

175% 11

Paragraph Text

175% 12

Paragraph Text

175% 13

Paragraph Text

175% 15

Paragraph Text

175% 16

Paragraph Text

175% 17

Paragraph Text

175% 18

Paragraph Text

175% 19

Paragraph Text

ScrollDisplacement.vueimport=features/features-scroll-displacement.vue
<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

Button 1

Paragraph Text

Button 2

Paragraph Text

Button 3

Paragraph Text

Button 4

Paragraph Text

Button 5

Paragraph Text

Button 6

Paragraph Text

Button 7

Paragraph Text

Button 8

Paragraph Text

CSSBtn.vueimport=features/features-css-btn.vue
<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

Button 1

Paragraph Text

Button 2

Paragraph Text

Button 3

Paragraph Text

Button 4

Paragraph Text

Button 5

Paragraph Text

Button 6

Paragraph Text

Button 7

Paragraph Text

Button 8

Paragraph Text

CSSContainer.vueimport=features/features-css-container.vue
<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;