hikari-components 0.2.1

Core UI components (40+) for the Hikari design system
// Custom DOM-based Scrollbar Styles
//
// This is a manually created scrollbar that doesn't use native webkit scrollbar.
// It uses a layered DOM structure to keep the track fixed while content scrolls.
//
// DOM Structure:
// container (relative, padding: 0)
//   └── wrapper (flex, row)     ← separation layer
//         ├── content_layer (flex:1, column, padding: original)  ← scrollable content
//         └── track (absolute, right: 0)  ← fixed scrollbar track
//               └── thumb (absolute)     ← draggable thumb

// ============================================================================
// IMPORTANT: Hide native scrollbars IMMEDIATELY on page load
// This prevents the flash of native scrollbar before custom one is set up
// ============================================================================

// Target elements that will get custom scrollbars
.hi-aside-content,
.hi-layout-aside-content,
.hi-layout-content,
.hi-layout-scrollable,
.hi-tree-virtual,
.hi-tabs-nav,
.hi-table-container,
.hi-sidebar,
.sidebar-nav,
.showcase-table-container,
.custom-scrollbar-content-vdom {
    // Hide native scrollbar IMMEDIATELY (before JS runs)
    // This prevents the flash of native scrollbar on page load
    &::-webkit-scrollbar {
        display: none;
        width: 0;
        height: 0;
    }
    -ms-overflow-style: none;  // IE/Edge
    scrollbar-width: none;  // Firefox
}

// ============================================================================
// Custom scrollbar styles (applied after JS wraps elements)
// ============================================================================

// Container with custom scrollbar
.custom-scrollbar-container {
    position: relative;

    // Hide native scrollbar
    &::-webkit-scrollbar {
        display: none;
    }
    -ms-overflow-style: none;
    scrollbar-width: none;
}

// Wrapper layer (flex container, provides positioning context for track)
.custom-scrollbar-wrapper {
    position: relative;  // Provide positioning context for absolute track
    pointer-events: none;  // Let events pass through to children
}

// Content layer (scrollable content)
.custom-scrollbar-content {
    pointer-events: auto;  // Re-enable events for content

    // Hide native scrollbar in content layer
    &::-webkit-scrollbar {
        display: none;
    }
    -ms-overflow-style: none;
    scrollbar-width: none;
}

// Scrollbar track (the rail) - absolutely positioned inside wrapper's right edge
.custom-scrollbar-track {
    position: absolute;  // Absolute positioning inside wrapper
    top: 0;
    right: 0;  // 贴着容器边缘
    bottom: 0;
    // width is set dynamically via JavaScript (4px → 8px)
    width: 4px; // Initial width
    border-radius: 4px;
    pointer-events: none;  // Don't capture clicks - let them pass through to content
    z-index: 100;
    transition: width 300ms cubic-bezier(0.25, 0.1, 0.25, 1),
                opacity 300ms cubic-bezier(0.25, 0.1, 0.25, 1);

    // Gradient background: dark at ends, light in middle
    // 16px - auto - (100% - 16px)
    background: linear-gradient(
        to bottom,
        var(--hi-scrollbar-track-dark) 16px,
        var(--hi-scrollbar-track-light) calc(50% - 8px),
        var(--hi-scrollbar-track-dark) calc(100% - 16px)
    );
}

// Hover state: background darkens (width is handled by JavaScript state machine)
.custom-scrollbar-track:hover {
    background: linear-gradient(
        to bottom,
        var(--hi-scrollbar-track-dark) 16px,
        var(--hi-scrollbar-track-light) calc(50% - 8px),
        var(--hi-scrollbar-track-dark) calc(100% - 16px)
    );
    // Add slight opacity increase on hover
    filter: brightness(1.1);
}

// Scrollbar thumb (the draggable handle)
.custom-scrollbar-thumb {
    position: absolute;
    right: 0;
    width: 100%;
    min-height: 20px;
    background: var(--hi-scrollbar-thumb);
    border-radius: 4px;
    cursor: pointer;
    pointer-events: auto;  // Re-enable clicks for thumb (overrides track's none)

    // Disable top transition to prevent drag lag
    transition: background 300ms cubic-bezier(0.25, 0.1, 0.25, 1),
                box-shadow 300ms cubic-bezier(0.25, 0.1, 0.25, 1),
                top 0s linear;
}

// Hover state: thumb glows
.custom-scrollbar-thumb:hover {
    background: var(--hi-scrollbar-thumb-hover);
    box-shadow: 0 0 14px var(--hi-scrollbar-thumb-hover);
}

// Active state: thumb being dragged (theme-aware filter)
.custom-scrollbar-thumb:active {
    background: var(--hi-scrollbar-thumb-hover);
    box-shadow: 0 0 16px var(--hi-scrollbar-thumb-hover);
    // Apply theme-aware brightness filter
    filter: var(--hi-scrollbar-thumb-active-filter);
}

// Hidden state (when content doesn't need scrolling)
.custom-scrollbar-track.custom-scrollbar-hidden {
    opacity: 0;
    pointer-events: none;
}

// Scroll-hover active state: wide track that auto-retracts after ~1s
// Uses CSS animation so no JS timer is needed (works reliably in WASM)
.custom-scrollbar-track.scrollbar-scrolling {
    animation: scrollbar-retract 1.3s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;

    // Override the base transition during animation
    transition: none;
}

@keyframes scrollbar-retract {
    0%,
    70% {
        width: 8px;
    }
    100% {
        width: 4px;
    }
}