← 一覧へ

Tab Menu / 29 サイバーホログラム|Cyber Hologram

デザイン見本

  • SYS.1
  • DATA.2
  • COMM.3
> Initialization sequence started.
> System 1 is fully operational.
> Accessing encrypted data packets.
> Data block 2 decrypted.
> Establishing secure channel.
> Communications online.

SFのようなスキャンラインやグリッチ表現を取り入れたホログラム風UIです。疑似要素や `clip-path` を用いて、選択状態のタブがバグったようにチラつくアニメーションや、コンテンツエリアが上から走査されるように現れる演出を加えています。

実装コード

HTML
<div class="tab-container">
    <ul>
        <li class="selected" data-id="tab-1" data-text="SYS.1">SYS.1</li>
        <li data-id="tab-2" data-text="DATA.2">DATA.2</li>
        <li data-id="tab-3" data-text="COMM.3">COMM.3</li>
    </ul>
    <div class="tab-content selected" id="tab-1">
        > Initialization sequence started.<br>
        > System 1 is fully operational.
    </div>
    <div class="tab-content" id="tab-2">
        > Accessing encrypted data packets.<br>
        > Data block 2 decrypted.
    </div>
    <div class="tab-content" id="tab-3">
        > Establishing secure channel.<br>
        > Communications online.
    </div>
</div>
CSS
.tab-container {
    background: #090a0f;
    padding: 20px;
    border-radius: 8px;
    border: 1px solid rgba(0, 255, 255, 0.2);
    box-shadow: inset 0 0 20px rgba(0, 255, 255, 0.05);
    overflow: hidden;
    position: relative;
    font-family: 'Courier New', Courier, monospace;
}

.tab-container::after {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    background: repeating-linear-gradient(
        0deg,
        rgba(0,0,0,0.1),
        rgba(0,0,0,0.1) 2px,
        transparent 2px,
        transparent 4px
    );
    pointer-events: none;
    z-index: 10;
}

.tab-container ul {
    margin: 0 0 20px 0;
    padding: 0;
    list-style: none;
    display: flex;
    gap: 8px;
    position: relative;
    z-index: 2;
}

.tab-container ul li {
    flex: 1;
    padding: 12px;
    text-align: center;
    cursor: pointer;
    background: rgba(0, 255, 255, 0.05);
    color: rgba(0, 255, 255, 0.5);
    border: 1px solid rgba(0, 255, 255, 0.3);
    text-transform: uppercase;
    font-weight: bold;
    letter-spacing: 2px;
    position: relative;
    transition: all 0.3s ease;
    clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}

.tab-container ul li::before {
    content: attr(data-text);
    position: absolute;
    top: 0; left: 0; width: 100%; height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #0ff;
    opacity: 0;
    transition: all 0.3s ease;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

.tab-container ul li:not(.selected):hover {
    background: rgba(0, 255, 255, 0.1);
    color: #0ff;
    text-shadow: 0 0 8px rgba(0, 255, 255, 0.8);
}

.tab-container ul li.selected {
    background: rgba(0, 255, 255, 0.2);
    color: transparent;
    border-color: #0ff;
    box-shadow: 0 0 15px rgba(0, 255, 255, 0.4);
}

.tab-container ul li.selected::before {
    opacity: 1;
    text-shadow: 2px 0 0 #f0f, -2px 0 0 #0ff;
    animation: glitchText 3s infinite linear;
}

@keyframes glitchText {
    0%, 100% { clip-path: polygon(0 0, 100% 0, 100% 15%, 0 15%); transform: translate(0); }
    10% { clip-path: polygon(0 15%, 100% 15%, 100% 30%, 0 30%); transform: translate(-2px, 1px); }
    20% { clip-path: polygon(0 30%, 100% 30%, 100% 45%, 0 45%); transform: translate(2px, -1px); }
    30% { clip-path: polygon(0 45%, 100% 45%, 100% 60%, 0 60%); transform: translate(-2px, 2px); }
    40% { clip-path: polygon(0 60%, 100% 60%, 100% 75%, 0 75%); transform: translate(2px, -2px); }
    50% { clip-path: polygon(0 75%, 100% 75%, 100% 90%, 0 90%); transform: translate(-1px, 1px); }
    60% { clip-path: polygon(0 90%, 100% 90%, 100% 100%, 0 100%); transform: translate(1px, -1px); }
    70%, 90% { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); transform: translate(0); }
}

.tab-container .tab-content {
    display: none;
    padding: 24px;
    background: rgba(0, 20, 30, 0.8);
    color: #0ff;
    border: 1px solid rgba(0, 255, 255, 0.2);
    border-left: 3px solid #0ff;
    min-height: 150px;
    position: relative;
    z-index: 2;
}

.tab-container .tab-content.selected {
    display: block;
    animation: cyberScan 0.5s ease-out;
}

@keyframes cyberScan {
    0% { opacity: 0; clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); }
    100% { opacity: 1; clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
}
JS
(function () {
    'use strict';
    document.addEventListener('DOMContentLoaded', () => {
        const tabBoxes = document.querySelectorAll('.tab-container');
        for (const box of tabBoxes) {
            const menuItems = box.querySelectorAll('ul li');
            const contents = box.querySelectorAll('.tab-content');
            for (const item of menuItems) {
                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');
                    }
                });
            }
        }
    });
})();