← 一覧へ

Tab Menu / 30 リキッドモーフィング|Liquid Morphing

デザイン見本

  • Tab 1
  • Tab 2
  • Tab 3
Good morning. This is the content of Tab 1.
Hello. This is the content of Tab 2.
Good evening. This is the content of Tab 3.

流れるような滑らかなアニメーションが特徴のデザインです。選択状態を示す白い背景がスライドアニメーションによって移動し、柔らかな `cubic-bezier` によってゼリーのように伸縮して見える効果を作っています。グラスモーフィズムとの組み合わせでモダンな印象を与えます。

実装コード

HTML
<div class="tab-container">
    <ul>
        <div class="liquid-bg"></div>
        <li class="selected" data-id="tab-1">Tab 1</li>
        <li data-id="tab-2">Tab 2</li>
        <li data-id="tab-3">Tab 3</li>
    </ul>
    <div class="tab-content selected" id="tab-1">
        Good morning. This is the content of Tab 1.
    </div>
    <div class="tab-content" id="tab-2">
        Hello. This is the content of Tab 2.
    </div>
    <div class="tab-content" id="tab-3">
        Good evening. This is the content of Tab 3.
    </div>
</div>
CSS
.tab-container {
    position: relative;
    padding: 20px;
    background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%);
    border-radius: 16px;
}

.tab-container ul {
    margin: 0 0 24px 0;
    padding: 8px;
    list-style: none;
    display: flex;
    position: relative;
    background: rgba(255, 255, 255, 0.4);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-radius: 50px;
    box-shadow: inset 0 2px 5px rgba(0,0,0,0.05);
}

.tab-container .liquid-bg {
    position: absolute;
    top: 8px;
    left: 8px;
    height: calc(100% - 16px);
    width: calc((100% - 16px) / 3);
    background: #fff;
    border-radius: 40px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
    z-index: 1;
}

.tab-container ul li {
    flex: 1;
    text-align: center;
    padding: 12px 20px;
    cursor: pointer;
    position: relative;
    z-index: 2;
    color: #555;
    font-weight: 600;
    transition: color 0.3s ease;
}

.tab-container ul li.selected {
    color: #6a11cb;
}

.tab-container .tab-content {
    display: none;
    padding: 30px;
    background: rgba(255, 255, 255, 0.7);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, 0.5);
    box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1);
    min-height: 150px;
    color: #333;
}

.tab-container .tab-content.selected {
    display: block;
    animation: liquidFadeIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

@keyframes liquidFadeIn {
    0% { opacity: 0; transform: translateY(10px) scale(0.98); border-radius: 30px; }
    100% { opacity: 1; transform: translateY(0) scale(1); border-radius: 16px; }
}
JS
(function () {
    'use strict';
    document.addEventListener('DOMContentLoaded', () => {
        const tabBoxes = document.querySelectorAll('.tab-container');
        for (const box of tabBoxes) {
            const liquidBg = box.querySelector('.liquid-bg');
            const menuItems = box.querySelectorAll('ul li');
            const contents = box.querySelectorAll('.tab-content');

            for (let idx = 0; idx < menuItems.length; idx++) {
                const item = menuItems[idx];
                item.addEventListener('click', () => {
                    for (const mItem of menuItems) {
                        mItem.classList.remove('selected');
                    }
                    item.classList.add('selected');
                    for (const content of contents) {
                        content.classList.remove('selected');
                    }
                    const targetKey = item.dataset.id;
                    const targetContent = document.getElementById(targetKey) || box.querySelector('#' + targetKey);
                    if (targetContent) {
                        targetContent.classList.add('selected');
                    }

                    if (liquidBg) {
                        const tX = 'translateX';
                        liquidBg.style.transform = tX + '(' + (idx * 100) + '%)';
                    }
                });
            }
        }
    });
})();