tabs.html
<p-tabs :titles="['First Tab', 'Second Tab', 'Third Tab', 'Fourth Tab', 'Fifth Tab']">
  <template #tabpanel-1>
    <h3>Elit hic amet neque.</h3>
    <p>Lorem nesciunt nemo excepturi aliquam asperiores sit repellat. Fugit libero enim quis vero harum Suscipit doloremque repellat est rerum id non Esse explicabo harum molestias quos corrupti nobis sit Voluptates</p>
  </template>

  <template #tabpanel-2>
    <h3>Sit voluptas illo beatae.</h3>
    <p>Ipsum eos quidem quae exercitationem distinctio. Deleniti exercitationem corrupti nihil porro consequuntur Repudiandae ut non libero expedita nulla? Autem obcaecati fugit blanditiis autem molestiae omnis? Similique odit eius eveniet veritatis.</p>
  </template>

  <template #tabpanel-3>
    <h3>Consectetur odit distinctio beatae!</h3>
    <p>Ipsum eaque ipsam asperiores consequatur est libero. Incidunt distinctio non quae veniam illum Laborum est harum sapiente vel suscipit maiores? Dicta quo velit eos ab distinctio, delectus! Autem sunt aperiam!</p>
  </template>

  <template #tabpanel-4>
    <h3>Consectetur veniam iste consequuntur</h3>
    <p>Elit laborum deleniti illo quidem tempora! Fugiat veritatis suscipit excepturi est odit ex? Adipisci sint quidem aliquid animi velit iure cupiditate Aliquam blanditiis quos culpa voluptatum velit Nemo cupiditate cupiditate</p>
  </template>

  <template #tabpanel-5>
    <h3>Elit sed eligendi id!</h3>
    <p>Ipsum placeat id architecto sapiente aliquid eaque! Itaque fugit animi necessitatibus aut incidunt Et eligendi error quis molestiae delectus Ut voluptate laudantium possimus corrupti tempora, veritatis! Veritatis facilis illo adipisci.</p>
  </template>
</p-tabs>
index.scss
@use '@/core' as *;

/**
 * this is a component specific property for animating.
 */
@property --tab__button-box-shadow {
  syntax: '<length>';
  inherits: false;
  initial-value: 0;
}

/**
 * @uses PTabs
 */
.tabs {
  $b: &;

  &__tablist {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    overflow: clip;

    @include at(sm) {
      border: solid var(--color-gray-300);
      border-width: 0 0 2px;
      gap: 2rem;
      justify-content: start;
    }

    @include at(md) {
      gap: 3rem;
    }
  }

  &__button {
    background: #00000000;
    border: 0;
    color: inherit;
    cursor: pointer;
    display: none;
    font: inherit;
    white-space: nowrap;
    outline-offset: 2px;
    transition:
      color var(--root-duration-fast) var(--root-ease-out),
      --tab__button-box-shadow var(--root-duration-fast) var(--root-ease-out);

    @include at(sm) {
      display: block;
      box-shadow: 0 var(--tab__button-box-shadow) var(--accent-color) inset;
      padding: 0 0 8px;

      &:hover {
        --tab__button-box-shadow: -2px;

        box-shadow: 0 var(--tab__button-box-shadow) var(--accent-color-400) inset;
        color: var(--color-gray-900);
      }
    }

    &.-selected {
      display: block;

      --tab__button-box-shadow: -8px;

      color: var(--color-gray-900);
    }
  }

  &__panel {
    padding: 1rem 0;
  }

  &__prev,
  &__next {
    flex-shrink: 0;
  }

  @include at(sm) {
    #{$b}__prev,
    #{$b}__next {
      background: red;
      display: none;
    }
  }

  &.-vertical {
    @include at(sm) {
      display: grid;
      grid: auto / auto 1fr;

      #{$b}__tablist {
        flex-direction: column;
        border-width: 0 2px 0 0;
        gap: 2rem;
      }

      #{$b}__button {
        padding: 0 1rem 0 0;
        text-align: right;
        box-shadow: var(--tab__button-box-shadow) 0 var(--accent-color) inset;
      }

      #{$b}__panel {
        padding: 0 1rem;
      }
    }
  }

  &.-solid {
    @include at(sm) {
      #{$b}__tablist {
        border: 0;
        gap: 0.5rem;
      }
      #{$b}__button {
        white-space: nowrap;
        background: var(--color-gray-100);
        border-radius: 0.5rem;
        box-shadow: 0 1px 1px 0 var(--color-gray-200);
        padding: 0.5rem 1rem;
        margin-bottom: 0.5rem;

        &:hover {
          background: #ffffff;
          box-shadow: var(--root-box-shadow-med);
          border-radius: 8px;
        }

        &.-selected {
          background: #ffffff;
          border-radius: 8px 8px 0 0;
          box-shadow: 0 0.5rem 0 #ffffff;
        }
      }

      #{$b}__panel {
        background: #ffffff;
        padding: 1rem;
      }
    }
  }
}
PTabs.vue
<script setup>
import { computed, ref, useTemplateRef, watchEffect } from 'vue'
import focusWithArrows from '/resources/js/composables/focusWithArrows'

const props = defineProps({
  titles: { type: Array, required: true },
})

const tablist = useTemplateRef('tablist')
const visibleTab = ref(0)

const focusedElement = focusWithArrows(tablist)

watchEffect(() => {
  if (focusedElement.value) {
    visibleTab.value = tablist.value.indexOf(focusedElement.value)
  }
})

const tabs = computed(() =>
  props.titles.map((title, i) => ({
    title,
    selected: i === visibleTab.value,
    tabindex: i === visibleTab.value ? 0 : -1,

    buttonId: `tab-${i + 1}`,
    panelId: `tabpanel-${i + 1}`,
  })),
)

const prev = () => {
  const selected = tabs.value.findIndex(e => e.selected)
  if (selected) {
    focusedElement.value = tablist.value[selected - 1]
  }
}

const next = () => {
  const selected = tabs.value.findIndex(e => e.selected)
  if (selected < tabs.value.length) {
    focusedElement.value = tablist.value[selected + 1]
  }
}
</script>

<template>
  <div class="tabs">
    <div class="tabs__tablist" role="tablist" aria-label="Sample Tabs">
      <button class="tabs__prev button -circle" @click="prev">
        <span class="srOnly">previous</span>
        <svg class="button__icon"><use href="/main-icons-sprite.svg#chevron-left" /></svg>
      </button>
      <button v-for="tab in tabs" :id="tab.buttonId" :key="tab.buttonId" ref="tablist" class="tabs__button" :class="{ '-selected': tab.selected }" role="tab" :aria-selected="tab.selected" :aria-controls="tab.panelId" :tabindex="tab.tabindex" v-text="tab.title" />
      <button class="tabs__next button -circle" @click="next">
        <span class="srOnly">next</span>
        <svg class="button__icon"><use href="/main-icons-sprite.svg#chevron-right" /></svg>
      </button>
    </div>

    <div v-for="tab in tabs" :id="tab.panelId" :key="tab.panelId" class="tabs__panel" role="tabpanel" :tabindex="tab.tabindex" :aria-labelledby="tab.buttonId" :hidden="!tab.selected">
      <slot :name="tab.panelId" />
    </div>
  </div>
</template>