<template>
  <div class="window">
    <div class="head">
      <div class="buttons">
        <div class="button -red"></div>
        <div class="button -yellow"></div>
        <div class="button -green"></div>
      </div>
      <div class="tabs-group">
        <draggable :list="$data.tabs" v-bind="dragOptions" :style="tabsWidth">
          <transition-group class="tabs" name="list-complete">
            <template v-for="tab in $data.tabs">
              <div
                :class="[active(tab.id), 'tab']"
                :key="tab.id"
                @mousedown="changeTab(tab.id)"
              >
                <p class="text">{{ tab.name }}</p>
                <div
                  class="close-button"
                  @mousedown="beforeCloseTab($event)"
                  @click="closeTab(tab.id)"
                ></div>
              </div>
            </template>
          </transition-group>
        </draggable>
        <div key="add-button" class="add-button" @click="addTab"></div>
      </div>
    </div>
    <div class="menu">
      <div class="buttons">
        <div class="arrows">
          <div class="arrow -back"></div>
          <div class="arrow -forward"></div>
        </div>
      </div>
      <input class="search" type="text" />
    </div>
    <div class="content">
      <template v-for="tab in $data.tabs">
        <div :class="[active(tab.id), 'tab-content']" :key="tab.id">
          <img class="image" :src="'https://picsum.photos/800/800?random=' + tab.id" />
        </div>
      </template>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentTabId: 1,
      nextTabId: 4,
      tabs: [
        {
          id: 1,
          name: "Hello CodePen",
        },
        {
          id: 2,
          name: "Hi CodePen",
        },
        {
          id: 3,
          name: "Hey CodePen",
        }
      ]
    };
  },
  computed: {
    active() {
      return (id) => id === this.$data.currentTabId && "-active";
    },
    dragOptions() {
      return {
        animation: 250,
        disabled: false,
        ghostClass: "-dragging"
      };
    },
    tabsWidth() {
      return {
        maxWidth: this.$data.tabs.length * 190 + "px",
        width: "100%",
        minWidth: "0px",
        transition: "max-width 0.2s",
      }
    }
  },
  methods: {
    changeTab(id) {
      this.$data.currentTabId = id;
    },
    beforeCloseTab(e) {
      e.stopPropagation();
    },
    addTab() {
      this.$data.tabs.push({
        id: this.$data.nextTabId,
        name: "new tab"
      });
      this.$data.currentTabId = this.$data.nextTabId;
      this.$data.nextTabId++;
    },
    closeTab(id) {
      this.$data.tabs.forEach((tab, index) => {
        tab.id === id &&
          this.$data.tabs.splice(index, 1) &&
          this.$data.currentTabId === id &&
          (this.$data.currentTabId = this.$data.tabs[index]
            ? this.$data.tabs[index].id
            : this.$data.tabs[index - 1].id);
      });
    }
  }
};
</script>

<style lang="scss">
$white: #fff;
$gray-dark: #dee1e6;
$gray-light: #e7eaef;

body {
  padding: 20px;
  font-family: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
  display: flex;
  align-items: center;
  height: 100vh;
  box-sizing: border-box;
  background: linear-gradient(135deg, rgb(250,250,255) 0%, rgb(240,240,246) 50%, rgb(230,230,237) 100%);
}

.window {
  width: 100%;
  max-width: 800px;
  max-height: 500px;
  height: 100%;
  border-radius: 8px;
  overflow: hidden;
  background-color: #fff;
  box-shadow: 4px 8px 20px rgba(0, 0, 0, 0.2);
  display: flex;
  flex-direction: column;
  margin: 20px auto;
  
  > .content {
    min-height: 0;
    height: 100%;
  }
}

.head {
  background-color: $gray-dark;
  display: flex;
  overflow: hidden;

  .buttons {
    display: flex;
    align-items: center;
    padding: 0 4px 0 12px;

    > .button {
      width: 12px;
      height: 12px;
      border-radius: 100%;

      &:not(:last-child) {
        margin-right: 6px;
      }

      &.-red {
        background-color: #fb5a59;
      }

      &.-yellow {
        background-color: #ffc139;
      }

      &.-green {
        background-color: #60d045;
      }
    }
  }
}

.menu {
  min-height: 36px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid $gray-dark;

  > .buttons {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px;

    > .arrows {
      display: flex;
      justify-content: space-between;
      width: 44px;

      > .arrow {
        width: 14px;
        height: 2px;
        background-color: #ccc;
        border-radius: 100px;
        position: relative;

        &::before,
        &::after {
          content: "";
          display: block;
          background-color: #ccc;
          position: absolute;
          width: 71%;
          height: 100%;
          border-radius: 100px;
          transform-origin: center left;
        }

        &::before {
          transform: rotate(45deg);
        }

        &::after {
          transform: rotate(-45deg);
        }

        &.-forward {
          transform: rotate(180deg);
        }
      }
    }
  }

  > .search {
    box-sizing: border-box;
    border-radius: 100px;
    border: 0;
    background-color: #f1f1f1;
    height: 26px;
    outline: 0;
    flex: 1;
    margin-right: 20px;
    padding: 0 12px;
    font-size: 12px;
    border: 2px solid transparent;

    &:focus {
      border: 2px solid #6dbce7;
    }
  }
}

.tabs-group {
  width: 100%;
  display: flex;
  align-items: center;
  padding-top: 8px;
  min-width: 0px;

  > .add-button {
    width: 14px;
    height: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: center;
    padding: 8px;
    margin: 0 4px 2px;
    border-radius: 100%;
    transition: background-color 0.5s;

    &:hover {
      background-color: #ccc;
    }

    &::before,
    &::after {
      content: "";
      position: absolute;
      display: block;
      width: 2px;
      height: 14px;
      background-color: #666;
    }

    &::before {
      transform: rotate(90deg);
    }
  }
}

.tabs {
  display: inline-flex;
  align-items: flex-end;
  box-sizing: border-box;
  padding: 0 8px;
  line-height: 1;
  position: relative;
  z-index: 1;
  user-select: none;
  width: 100%;
}

.tab {
  position: relative;
  background-color: transparent;
  padding: 14px 12px;
  border-radius: 10px 10px 0 0;
  font-size: 12px;
  display: block;
  max-width: 160px;
  transition: background-color 0.2s;
  cursor: default;
  position: relative;
  margin-right: -1px;
  white-space: nowrap;
  flex: 1;
  overflow: hidden;
  display: flex;
  justify-content: space-between;
  align-items: center;

  &:hover {
    background-color: $gray-light;
    
    &::before {
      background-color: $gray-light;
      right: 0;
    }
  }

  &.-active {
    background-color: $white;
    
    &::before {
      background-color: $white;
      right: 0;
    }
  }

  &.-dragging {
    opacity: 0;
  }

  &::after {
    content: "";
    display: block;
    height: 20px;
    width: 1px;
    background-color: #888;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 0;
    z-index: -1;
  }
  
  &::before {
    content: "";
    display: block;
    height: 20px;
    width: 30px;
    position: absolute;
    right: 1px;
    background-color: $gray-dark;
    transition: background-color 0.2s;
  }

  > .close-button {
    width: 10px;
    height: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    transform: rotate(45deg);
    border-radius: 100%;
    padding: 4px;
    position: absolute;
    right: 8px;

    &:hover {
      background-color: #ddd;
    }

    &::before,
    &::after {
      content: "";
      position: absolute;
      display: block;
      width: 2px;
      height: 12px;
      background-color: #666;
    }

    &::before {
      transform: rotate(90deg);
    }
  }
}

.tab-content {
  display: none;
  padding: 20px;
  height: 100%;
  box-sizing: border-box;

  &.-active {
    display: block;
  }
  
  > .image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}
</style>
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js