CSS Files Styling

This commit is contained in:
2025-11-28 17:43:48 +00:00
parent 8cdb39afbe
commit 4d17b599b3
54 changed files with 4817 additions and 1764 deletions

View File

@@ -1,13 +1,15 @@
<script lang="ts">
import { mode, colorTheme, toggleMode, setColorTheme, themeOptions, themeColors, type ColorTheme } from '$lib/stores/theme';
import { page } from '$app/stores';
import { fly, fade } from 'svelte/transition';
import { fly, fade, slide } from 'svelte/transition';
import { user, navigation, colorPalette } from '$lib/config';
import Icon from '@iconify/svelte';
import { onMount, onDestroy } from 'svelte';
import '$lib/assets/css/navbar-waybar.css';
// State
let themeDropdownOpen = $state(false);
let mobileMenuOpen = $state(false);
let currentTime = $state(new Date());
// Derived values (Svelte 5 runes)
@@ -69,9 +71,14 @@
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') {
themeDropdownOpen = false;
mobileMenuOpen = false;
}
}
function closeMobileMenu() {
mobileMenuOpen = false;
}
function getThemeIcon(theme: ColorTheme): string {
switch (theme) {
case 'arch': return 'mdi:arch';
@@ -105,8 +112,18 @@
<img src="/favicon.png" alt="Blob Icon" width="16" />
</a>
<!-- Workspaces -->
<div class="module workspaces">
<!-- Mobile menu toggle -->
<button
class="mobile-menu-toggle"
onclick={() => mobileMenuOpen = !mobileMenuOpen}
aria-expanded={mobileMenuOpen}
aria-label="Toggle navigation menu"
>
<Icon icon={mobileMenuOpen ? 'mdi:close' : 'mdi:menu'} width="20" />
</button>
<!-- Workspaces (desktop) -->
<div class="module workspaces desktop-only">
{#each navigation as nav}
{@const isActive = currentPath === nav.path || (nav.path !== '/' && currentPath.startsWith(nav.path))}
{#if nav.external}
@@ -133,8 +150,8 @@
{/each}
</div>
<!-- Current window title -->
<div class="module window-title">
<!-- Current window title (desktop) -->
<div class="module window-title desktop-only">
<span class="title-icon">
{#if currentPath === '/'}
<Icon icon="mdi:home" width="14" />
@@ -209,296 +226,89 @@
</div>
<!-- Backdrop -->
{#if themeDropdownOpen}
{#if themeDropdownOpen || mobileMenuOpen}
<button
class="backdrop"
transition:fade={{ duration: 100 }}
onclick={() => themeDropdownOpen = false}
onclick={() => { themeDropdownOpen = false; mobileMenuOpen = false; }}
aria-label="Close"
></button>
{/if}
</nav>
<style>
.waybar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
height: var(--navbar-height, 40px);
background: var(--bar-bg);
border-bottom: 1px solid var(--bar-border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0.5rem;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.8rem;
gap: 0.5rem;
}
.bar-left,
.bar-center,
.bar-right {
display: flex;
align-items: center;
gap: 0.5rem;
}
.bar-left {
flex: 1;
}
.bar-right {
flex: 1;
justify-content: flex-end;
}
/* Modules */
.module {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.35rem 0.6rem;
background: var(--bar-bg-module);
border-radius: 4px;
color: var(--bar-text);
transition: all 0.15s ease;
}
.module:hover {
background: color-mix(in srgb, var(--bar-primary) 20%, var(--bar-bg-module));
}
/* module-group removed (minimal waybar look uses individual modules) */
/* Launcher */
.launcher {
color: var(--bar-primary);
padding: 0.35rem 0.5rem;
}
.launcher:hover {
color: var(--bar-accent);
}
/* Workspaces */
.workspaces {
display: flex;
gap: 0.25rem;
padding: 0.2rem 0.3rem;
}
.workspace {
display: flex;
align-items: center;
justify-content: center;
gap: 0.3rem;
padding: 0.3rem 0.6rem;
background: transparent;
border-radius: 4px;
color: var(--bar-muted);
font-size: 0.75rem;
font-weight: 500;
transition: all 0.15s ease;
text-decoration: none;
white-space: nowrap;
}
.workspace:hover {
background: color-mix(in srgb, var(--bar-primary) 25%, transparent);
color: var(--bar-text);
}
.workspace.active {
background: var(--bar-primary);
color: var(--bar-bg);
font-weight: 600;
}
.workspace .ws-name {
font-size: 0.75rem;
}
.workspace.external {
color: var(--bar-muted);
font-size: 0.7rem;
}
.workspace.external:hover {
color: var(--bar-accent);
}
/* Window title */
.window-title {
color: var(--bar-muted);
font-size: 0.75rem;
max-width: 200px;
overflow: hidden;
}
.title-icon {
display: flex;
color: var(--bar-primary);
}
.title-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Clock */
.clock {
background: var(--bar-bg-module);
padding: 0.35rem 0.75rem;
}
.clock .time {
font-weight: 600;
color: var(--bar-text);
}
.clock .date {
color: var(--bar-muted);
font-size: 0.7rem;
margin-left: 0.5rem;
}
/* System modules removed */
/* Theme selector */
.theme-selector {
position: relative;
padding: 0;
background: transparent;
}
.theme-trigger {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
background: var(--bar-bg-module);
border: none;
border-radius: 4px;
color: var(--bar-text);
cursor: pointer;
transition: all 0.15s ease;
margin: 5px;
}
.theme-trigger:hover {
background: color-mix(in srgb, var(--bar-primary) 25%, var(--bar-bg-module));
color: var(--bar-primary);
}
.theme-dropdown {
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
min-width: 160px;
background: var(--bar-bg);
border: 1px solid var(--bar-border);
border-radius: 8px;
padding: 0.4rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
z-index: 1001;
}
.dropdown-header {
padding: 0.4rem 0.6rem;
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--bar-muted);
}
.dropdown-divider {
height: 1px;
background: var(--bar-border);
margin: 0.3rem 0;
}
.theme-option {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.5rem 0.6rem;
background: transparent;
border: none;
border-radius: 4px;
color: var(--bar-text);
font-family: inherit;
font-size: 0.75rem;
text-align: left;
cursor: pointer;
transition: all 0.15s ease;
}
.theme-option:hover {
background: color-mix(in srgb, var(--bar-primary) 15%, transparent);
}
.theme-option.active {
color: var(--bar-primary);
}
.theme-option span {
flex: 1;
}
:global(.check) {
color: var(--bar-primary);
}
/* Backdrop */
.backdrop {
position: fixed;
inset: 0;
background: transparent;
z-index: 999;
border: none;
cursor: default;
}
/* Responsive */
@media (max-width: 768px) {
.waybar {
padding: 0 0.25rem;
}
.window-title {
display: none;
}
.clock .date {
display: none;
}
}
@media (max-width: 480px) {
.workspaces {
gap: 0.15rem;
}
.workspace {
padding: 0.25rem 0.4rem;
font-size: 0.65rem;
}
.workspace .ws-name {
font-size: 0.65rem;
}
}
</style>
<!-- Mobile menu dropdown -->
{#if mobileMenuOpen}
<div
class="mobile-menu"
transition:slide={{ duration: 200 }}
style="
--bar-bg: {$themeColors.background};
--bar-bg-module: {$themeColors.backgroundLight};
--bar-border: {$themeColors.border};
--bar-text: {$themeColors.text};
--bar-primary: {$themeColors.primary};
--bar-accent: {$themeColors.accent};
--bar-muted: {$themeColors.textMuted};
"
>
<div class="mobile-nav-links">
{#each navigation as nav}
{@const isActive = currentPath === nav.path || (nav.path !== '/' && currentPath.startsWith(nav.path))}
{#if nav.external}
<a
href={nav.path}
class="mobile-nav-link external"
target="_blank"
rel="noopener noreferrer"
onclick={closeMobileMenu}
>
<Icon icon="mdi:open-in-new" width="16" />
<span>{nav.name}</span>
</a>
{:else}
<a
href={nav.path}
class="mobile-nav-link"
class:active={isActive}
onclick={closeMobileMenu}
>
{#if nav.path === '/'}
<Icon icon="mdi:home" width="16" />
{:else if nav.path === '/portfolio'}
<Icon icon="mdi:folder-multiple" width="16" />
{:else if nav.path === '/models'}
<Icon icon="mdi:cube-outline" width="16" />
{:else if nav.path === '/projects'}
<Icon icon="mdi:trophy" width="16" />
{:else}
<Icon icon="mdi:file" width="16" />
{/if}
<span>{nav.name}</span>
</a>
{/if}
{/each}
</div>
<div class="mobile-menu-divider"></div>
<div class="mobile-theme-section">
<div class="mobile-section-header">Theme</div>
<div class="mobile-theme-options">
{#each themeOptions as option}
<button
class="mobile-theme-btn"
class:active={$colorTheme === option.value}
onclick={() => { handleThemeSelect(option.value); closeMobileMenu(); }}
>
<Icon icon={getThemeIcon(option.value)} width="18" />
<span>{option.label}</span>
</button>
{/each}
</div>
<button class="mobile-mode-btn" onclick={() => { toggleMode(); closeMobileMenu(); }}>
<Icon icon={$mode === 'dark' ? 'mdi:weather-sunny' : 'mdi:weather-night'} width="18" />
<span>{$mode === 'dark' ? 'Switch to Light' : 'Switch to Dark'}</span>
</button>
</div>
</div>
{/if}