fix(BUG-2): sidebar pure Tailwind, revert logo, fix mobile translate
- Replace all inline style= with Tailwind classes (except progress bar width) - Remove onmouseenter/onmouseleave JS hover handlers → hover: Tailwind classes - Revert ws-logo to /brand/logo-mark.svg (user requirement) - Fix mobile: 'transform: translateX(0)' → 'translate: 0' (Tailwind v4 uses CSS translate property, not transform, for -translate-x-full) - Move sidebar transition to .nim-sidebar critical CSS rule - Switch --nim-* CSS vars to --color-* tokens already in app.css - nav-item hover uses :not(.nim-nav-active) to avoid conflict with active state - Workspace/Account section labels, icon partial, AI badge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
d2d43e1097
commit
e36ecb1dba
|
|
@ -33,44 +33,34 @@
|
|||
<aside
|
||||
x-data="{ wsOpen: false }"
|
||||
class="nim-sidebar fixed inset-y-0 left-0 h-screen flex flex-col z-40 -translate-x-full md:translate-x-0"
|
||||
style="transition: width .28s cubic-bezier(.4,0,.2,1), transform .28s cubic-bezier(.4,0,.2,1);"
|
||||
:style="$store.sidebar?.mobileOpen ? 'transform: translateX(0)' : null"
|
||||
:style="$store.sidebar?.mobileOpen ? 'translate: 0' : null"
|
||||
>
|
||||
|
||||
{{-- ═══ Header ═══ --}}
|
||||
<header class="nim-sb-header flex-shrink-0 px-3.5 py-3 border-b" style="border-color:var(--nim-border); background:linear-gradient(180deg,rgba(255,255,255,0.015),transparent);" @click.outside="wsOpen = false">
|
||||
<header class="flex-shrink-0 px-3 py-3 border-b border-white/[.06]" @click.outside="wsOpen = false">
|
||||
<button
|
||||
@click="wsOpen = !wsOpen"
|
||||
@click="$store.sidebar?.isCollapsed && !$store.sidebar?.isMobile ? $store.sidebar.toggle() : (wsOpen = !wsOpen)"
|
||||
:aria-expanded="wsOpen.toString()"
|
||||
class="ws-trigger w-full flex items-center gap-3 px-2.5 py-2 rounded-[10px] transition-colors duration-150"
|
||||
style="hover:background:var(--nim-surface-3)"
|
||||
onmouseenter="this.style.background='var(--nim-surface-3)'"
|
||||
onmouseleave="this.style.background=''"
|
||||
class="ws-trigger w-full flex items-center gap-3 px-2.5 py-2 rounded-xl hover:bg-s2 transition-colors duration-150"
|
||||
>
|
||||
<span class="ws-logo flex-shrink-0 w-9 h-9 rounded-[9px] flex items-center justify-center text-white font-bold text-[18px] relative overflow-hidden"
|
||||
style="background:var(--nim-brand-grad);box-shadow:0 6px 16px -6px rgba(168,85,247,.55),inset 0 1px 0 rgba(255,255,255,.25)">
|
||||
<span style="position:relative;z-index:1">N</span>
|
||||
<span style="position:absolute;inset:0;background:radial-gradient(circle at 30% 20%,rgba(255,255,255,.35),transparent 50%);pointer-events:none"></span>
|
||||
<img src="/brand/logo-mark.svg" alt="Nimuli" class="ws-logo w-8 h-8 flex-shrink-0 rounded-lg">
|
||||
<span class="ws-text flex-1 min-w-0 text-left grid leading-snug">
|
||||
<span class="ws-brand text-accent-gradient font-bold text-sm tracking-tight">nimuli</span>
|
||||
<span class="ws-name text-xs text-t2 truncate">{{ $workspace?->name ?? 'Select workspace' }}</span>
|
||||
</span>
|
||||
<span class="ws-text flex-1 min-w-0 text-left" style="display:grid;line-height:1.2">
|
||||
<span class="ws-brand font-bold text-[15px] tracking-tight"
|
||||
style="background:var(--nim-brand-grad);-webkit-background-clip:text;background-clip:text;color:transparent">nimuli</span>
|
||||
<span class="ws-name text-[12.5px] truncate" style="color:var(--nim-text-muted)">
|
||||
{{ $workspace?->name ?? 'Select workspace' }}
|
||||
</span>
|
||||
</span>
|
||||
<svg class="ws-chevron flex-shrink-0 w-4 h-4 transition-transform duration-200" :class="wsOpen && 'rotate-180'"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="ws-chevron w-4 h-4 text-t3 flex-shrink-0 transition-transform duration-200"
|
||||
:class="wsOpen && 'rotate-180'"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- Mobile close --}}
|
||||
<button @click="$store.sidebar.closeMobile()"
|
||||
class="md:hidden absolute right-3 top-4 p-2 rounded-lg transition-colors"
|
||||
style="color:var(--nim-text-muted)"
|
||||
onmouseenter="this.style.background='rgba(255,255,255,0.05)'"
|
||||
onmouseleave="this.style.background=''">
|
||||
<button
|
||||
@click="$store.sidebar.closeMobile()"
|
||||
class="md:hidden absolute right-3 top-3.5 p-2 rounded-lg text-t3 hover:text-t1 hover:bg-s2 transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" viewBox="0 0 20 20" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6l8 8M6 14L14 6"/>
|
||||
</svg>
|
||||
|
|
@ -85,8 +75,7 @@
|
|||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 -translate-y-1"
|
||||
class="mt-2 rounded-[10px] overflow-hidden border"
|
||||
style="background:var(--nim-surface-2);border-color:var(--nim-border)"
|
||||
class="mt-2 bg-s2 border border-white/[.06] rounded-xl overflow-hidden"
|
||||
x-cloak
|
||||
>
|
||||
@forelse($workspaces as $ws)
|
||||
|
|
@ -94,32 +83,29 @@
|
|||
href="{{ route('w.links.index', $ws->ulid) }}"
|
||||
wire:navigate
|
||||
@click="wsOpen = false"
|
||||
class="flex items-center gap-2.5 px-3.5 py-3 text-[13.5px] transition-colors border-b"
|
||||
style="border-color:var(--nim-border);{{ $workspace?->id === $ws->id ? 'color:var(--nim-text);font-weight:500' : 'color:var(--nim-text-muted)' }}"
|
||||
onmouseenter="this.style.background='rgba(255,255,255,0.03)'"
|
||||
onmouseleave="this.style.background=''"
|
||||
class="flex items-center gap-2.5 px-3.5 py-2.5 text-sm border-b border-white/[.06] transition-colors hover:bg-s3
|
||||
{{ $workspace?->id === $ws->id ? 'text-t1 font-medium' : 'text-t2' }}"
|
||||
>
|
||||
<span class="w-[22px] h-[22px] rounded-[6px] flex items-center justify-center text-white font-bold text-[11px] flex-shrink-0"
|
||||
style="background:var(--nim-brand-grad)">{{ strtoupper(substr($ws->name, 0, 1)) }}</span>
|
||||
<span class="w-6 h-6 rounded-md flex items-center justify-center text-white font-bold text-xs flex-shrink-0 bg-accent-gradient">
|
||||
{{ strtoupper(substr($ws->name, 0, 1)) }}
|
||||
</span>
|
||||
<span class="flex-1 truncate">{{ $ws->name }}</span>
|
||||
@if($workspace?->id === $ws->id)
|
||||
<svg class="w-4 h-4 flex-shrink-0" style="color:var(--nim-accent-2)"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="w-3.5 h-3.5 flex-shrink-0 text-accent" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
@endif
|
||||
</a>
|
||||
@empty
|
||||
<div class="px-3.5 py-3 text-xs" style="color:var(--nim-text-dim)">No workspaces yet</div>
|
||||
<div class="px-3.5 py-2.5 text-xs text-t3">No workspaces yet</div>
|
||||
@endforelse
|
||||
<button
|
||||
@click="$dispatch('openModal', {component: 'modals.create-workspace'}); wsOpen = false"
|
||||
class="w-full flex items-center gap-2.5 px-3.5 py-3 text-[13.5px] transition-colors"
|
||||
style="color:var(--nim-text-muted)"
|
||||
onmouseenter="this.style.background='rgba(255,255,255,0.03)';this.style.color='var(--nim-text)'"
|
||||
onmouseleave="this.style.background='';this.style.color='var(--nim-text-muted)'"
|
||||
class="w-full flex items-center gap-2.5 px-3.5 py-2.5 text-sm text-t2 hover:text-t1 hover:bg-s3 transition-colors text-left"
|
||||
>
|
||||
<svg class="w-[18px] h-[18px] flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||||
</svg>
|
||||
<span>New Workspace</span>
|
||||
|
|
@ -131,7 +117,9 @@
|
|||
<nav class="nim-sb-nav flex-1 overflow-y-auto overflow-x-hidden px-2.5 py-2.5" aria-label="Main">
|
||||
|
||||
@if($workspace)
|
||||
<div class="nav-label px-3 pt-2 pb-1.5 text-[11px] uppercase tracking-[.08em] font-medium" style="color:var(--nim-text-dim)">Workspace</div>
|
||||
<div class="nav-label px-3 pt-1 pb-1.5 text-[11px] uppercase tracking-widest font-medium text-t3">
|
||||
Workspace
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@foreach($wsNav as $item)
|
||||
|
|
@ -145,17 +133,19 @@
|
|||
data-nav-link="{{ $item['route'] }}"
|
||||
@isset($item['exact']) data-nav-exact="1" @endisset
|
||||
data-label="{{ $item['label'] }}"
|
||||
class="nav-item relative flex items-center gap-3 px-3 py-[9px] rounded-lg text-sm font-medium w-full {{ $isActive ? 'nim-nav-active' : '' }}"
|
||||
class="nav-item relative flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium w-full {{ $isActive ? 'nim-nav-active' : '' }}"
|
||||
>
|
||||
<span class="w-[18px] h-[18px] flex-shrink-0">
|
||||
@include('partials.sidebar-icon', ['icon' => $item['icon']])
|
||||
</span>
|
||||
<span class="nav-text flex-1 truncate">{{ $item['label'] }}</span>
|
||||
<span class="nav-text truncate">{{ $item['label'] }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
|
||||
@if($workspace && count($accountNav))
|
||||
<div class="nav-label px-3 pt-4 pb-1.5 text-[11px] uppercase tracking-[.08em] font-medium" style="color:var(--nim-text-dim)">Account</div>
|
||||
<div class="nav-label px-3 pt-4 pb-1.5 text-[11px] uppercase tracking-widest font-medium text-t3">
|
||||
Account
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@foreach($accountNav as $item)
|
||||
|
|
@ -168,15 +158,14 @@
|
|||
wire:navigate
|
||||
data-nav-link="{{ $item['route'] }}"
|
||||
data-label="{{ $item['label'] }}"
|
||||
class="nav-item relative flex items-center gap-3 px-3 py-[9px] rounded-lg text-sm font-medium w-full {{ $isActive ? 'nim-nav-active' : '' }}"
|
||||
class="nav-item relative flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium w-full {{ $isActive ? 'nim-nav-active' : '' }}"
|
||||
>
|
||||
<span class="w-[18px] h-[18px] flex-shrink-0">
|
||||
@include('partials.sidebar-icon', ['icon' => $item['icon']])
|
||||
</span>
|
||||
<span class="nav-text flex-1 truncate">{{ $item['label'] }}</span>
|
||||
<span class="nav-text truncate">{{ $item['label'] }}</span>
|
||||
@if($item['icon'] === 'ai')
|
||||
<span class="nav-badge text-[10.5px] px-1.5 py-0.5 rounded-full font-semibold tracking-wide"
|
||||
style="background:var(--nim-accent-bg-strong);color:var(--nim-accent-2)">New</span>
|
||||
<span class="nav-badge ml-auto text-[10px] px-1.5 py-0.5 rounded-full font-semibold bg-accent/10 text-accent">New</span>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
|
|
@ -184,20 +173,20 @@
|
|||
{{-- Plan usage --}}
|
||||
@if($workspace && $plan)
|
||||
<div class="nav-plan-usage px-1 pt-4 pb-1">
|
||||
<div class="p-3 rounded-xl border" style="background:var(--nim-surface-2);border-color:var(--nim-border)">
|
||||
<div class="p-3 rounded-xl bg-s2 border border-white/[.06]">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-medium" style="color:var(--nim-text-muted)">{{ ucfirst($plan->name) }} Plan</span>
|
||||
<span class="text-xs font-medium text-t2">{{ ucfirst($plan->name) }} Plan</span>
|
||||
@if(str_contains(strtolower($plan->name), 'free'))
|
||||
<a href="{{ Route::has('w.billing.index') ? route('w.billing.index', $wsUlid) : '#' }}"
|
||||
wire:navigate class="text-xs hover:underline" style="color:var(--nim-accent-2)">Upgrade</a>
|
||||
wire:navigate class="text-xs text-accent hover:underline">Upgrade</a>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex justify-between mb-1.5">
|
||||
<span class="text-xs" style="color:var(--nim-text-dim)">Links</span>
|
||||
<span class="text-xs" style="color:var(--nim-text-muted)">{{ number_format($linksUsed) }} / {{ number_format($linkLimit) }}</span>
|
||||
<span class="text-xs text-t3">Links</span>
|
||||
<span class="text-xs text-t2">{{ number_format($linksUsed) }} / {{ number_format($linkLimit) }}</span>
|
||||
</div>
|
||||
<div class="w-full h-1 rounded-full" style="background:var(--nim-surface-3)">
|
||||
<div class="h-1 rounded-full transition-all" style="width:{{ $usagePct }}%;background:var(--nim-accent)"></div>
|
||||
<div class="w-full h-1 rounded-full bg-s3">
|
||||
<div class="h-1 rounded-full bg-accent transition-all" style="width: {{ $usagePct }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -206,17 +195,16 @@
|
|||
</nav>
|
||||
|
||||
{{-- ═══ Footer ═══ --}}
|
||||
<footer class="nim-sb-footer flex-shrink-0 border-t" style="border-color:var(--nim-border);background:linear-gradient(0deg,rgba(255,255,255,.012),transparent)">
|
||||
<footer class="flex-shrink-0 border-t border-white/[.06]">
|
||||
|
||||
{{-- Collapse toggle (desktop only) --}}
|
||||
<button
|
||||
@click="$store.sidebar.toggle()"
|
||||
class="collapse-row max-md:hidden w-full flex items-center gap-2.5 px-[18px] py-2.5 text-[13px] border-b transition-colors duration-150"
|
||||
style="color:var(--nim-text-dim);border-color:var(--nim-border)"
|
||||
onmouseenter="this.style.color='var(--nim-text)'"
|
||||
onmouseleave="this.style.color='var(--nim-text-dim)'"
|
||||
class="collapse-row max-md:hidden w-full flex items-center gap-3 px-[18px] py-2.5 text-sm text-t3 hover:text-t1 border-b border-white/[.06] transition-colors duration-150"
|
||||
>
|
||||
<svg class="w-4 h-4 flex-shrink-0 transition-transform duration-250" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="w-4 h-4 flex-shrink-0 transition-transform duration-200"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="15 18 9 12 15 6"/>
|
||||
</svg>
|
||||
<span class="collapse-text">Collapse</span>
|
||||
|
|
@ -224,21 +212,20 @@
|
|||
|
||||
{{-- User row --}}
|
||||
@auth
|
||||
<div class="user-row flex items-center gap-3 px-3.5 py-3.5 min-w-0">
|
||||
<div class="nim-avatar flex-shrink-0 w-9 h-9 rounded-full flex items-center justify-center text-white font-semibold text-[13px]"
|
||||
style="background:linear-gradient(135deg,#8b5cf6,#6366f1);box-shadow:inset 0 1px 0 rgba(255,255,255,.18)">
|
||||
<div class="user-row flex items-center gap-3 px-3.5 py-3 min-w-0">
|
||||
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-accent flex items-center justify-center text-white text-xs font-semibold">
|
||||
{{ $initials ?: strtoupper(substr(auth()->user()->email, 0, 2)) }}
|
||||
</div>
|
||||
<div class="user-info flex-1 min-w-0" style="line-height:1.2">
|
||||
<div class="text-[13.5px] font-semibold truncate" style="color:var(--nim-text)">{{ auth()->user()->name }}</div>
|
||||
<div class="text-[12px] truncate" style="color:var(--nim-text-dim)">{{ auth()->user()->email }}</div>
|
||||
<div class="user-info flex-1 min-w-0">
|
||||
<div class="text-sm font-medium text-t1 truncate">{{ auth()->user()->name }}</div>
|
||||
<div class="text-xs text-t3 truncate">{{ auth()->user()->email }}</div>
|
||||
</div>
|
||||
<form class="user-logout-btn flex-shrink-0" method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" title="Sign out" aria-label="Sign out"
|
||||
class="p-2 rounded-lg transition-colors duration-150"
|
||||
style="color:var(--nim-text-dim)">
|
||||
<svg class="w-[18px] h-[18px]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
class="p-2 rounded-lg text-t3 hover:text-red-400 hover:bg-red-400/10 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
|
||||
<polyline points="16 17 21 12 16 7"/>
|
||||
<line x1="21" y1="12" x2="9" y2="12"/>
|
||||
|
|
@ -247,6 +234,7 @@
|
|||
</form>
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
</footer>
|
||||
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -100,10 +100,11 @@
|
|||
/* Sidebar shell */
|
||||
.nim-sidebar {
|
||||
width: var(--nim-sb-w);
|
||||
background: var(--nim-surface);
|
||||
border-right: 1px solid var(--nim-border);
|
||||
background: var(--color-s1);
|
||||
border-right: 1px solid var(--color-b1);
|
||||
overflow-x: hidden;
|
||||
font-family: 'Geist', 'Inter', ui-sans-serif, system-ui, sans-serif;
|
||||
transition: width .28s cubic-bezier(.4,0,.2,1), translate .28s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
|
||||
/* Content margin */
|
||||
|
|
@ -114,20 +115,20 @@
|
|||
}
|
||||
|
||||
/* Nav items */
|
||||
.nav-item { color: var(--nim-text-muted); }
|
||||
.nav-item:hover { background: var(--nim-surface-3) !important; color: var(--nim-text) !important; }
|
||||
.nav-item { color: var(--color-t2); text-decoration: none; }
|
||||
.nav-item:hover:not(.nim-nav-active) { background: var(--color-s3); color: var(--color-t1); }
|
||||
.nav-item.nim-nav-active {
|
||||
background: var(--nim-accent-bg);
|
||||
color: var(--nim-accent-2);
|
||||
box-shadow: inset 0 0 0 1px rgba(139,92,246,.18);
|
||||
background: rgba(124,58,237,.12);
|
||||
color: var(--color-accent);
|
||||
box-shadow: inset 0 0 0 1px rgba(124,58,237,.2);
|
||||
}
|
||||
.nav-item.nim-nav-active svg { color: var(--nim-accent-2); }
|
||||
.nav-item.nim-nav-active svg { color: var(--color-accent); }
|
||||
.nav-item.nim-nav-active::before {
|
||||
content: ''; position: absolute;
|
||||
left: -10px; top: 50%; transform: translateY(-50%);
|
||||
width: 3px; height: 18px;
|
||||
background: var(--nim-accent); border-radius: 0 3px 3px 0;
|
||||
box-shadow: 0 0 12px var(--nim-accent-glow);
|
||||
background: var(--color-accent); border-radius: 0 3px 3px 0;
|
||||
box-shadow: 0 0 12px rgba(124,58,237,.5);
|
||||
}
|
||||
|
||||
/* Nav scrollbar */
|
||||
|
|
@ -136,8 +137,7 @@
|
|||
.nim-sb-nav::-webkit-scrollbar-thumb { background: var(--nim-border-strong); border-radius: 3px; }
|
||||
.nim-sb-nav::-webkit-scrollbar-track { background: transparent; }
|
||||
|
||||
/* Logout hover */
|
||||
.user-logout-btn button:hover { color: #ef4444 !important; background: rgba(239,68,68,.08); border-radius: .5rem; }
|
||||
/* Logout hover (Tailwind handles this via hover:text-red-400 hover:bg-red-400/10) */
|
||||
|
||||
/* Collapse state — desktop only */
|
||||
@media (min-width: 768px) {
|
||||
|
|
@ -162,10 +162,10 @@
|
|||
html[data-sidebar-collapsed] .nav-item:hover::after {
|
||||
content: attr(data-label);
|
||||
position: absolute; left: calc(100% + 12px); top: 50%; transform: translateY(-50%);
|
||||
background: var(--nim-surface-3); color: var(--nim-text);
|
||||
background: var(--color-s3); color: var(--color-t1);
|
||||
padding: 6px 10px; border-radius: 6px; font-size: 12.5px;
|
||||
white-space: nowrap; pointer-events: none;
|
||||
border: 1px solid var(--nim-border-strong);
|
||||
border: 1px solid var(--color-b2);
|
||||
box-shadow: 0 8px 24px -6px rgba(0,0,0,.5); z-index: 100;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue