fix: Alpine double-load, FOUC, purple accent, logo/favicon
- Remove alpinejs devDep; Livewire v3 owns Alpine lifecycle - Register sidebar + theme stores via alpine:init event - @persist sidebar across wire:navigate to prevent re-render - Inline critical CSS for FOUC prevention on initial load - Purple #7c3aed accent + pink #ec4899 gradient in both themes - Light-mode border visibility fix (solid color, not opacity) - bg-accent-gradient / text-accent-gradient / border-accent-gradient utilities - Logo mark SVG + full wordmark variants (dark/light/neutral) - favicon.svg, favicon PNGs, apple-touch-icon, site.webmanifest - Sidebar uses logo-mark.svg + text-accent-gradient wordmark Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
d0365fd34f
commit
00673f3119
|
|
@ -6,7 +6,6 @@
|
|||
"": {
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"alpinejs": "^3.15.12",
|
||||
"axios": "^1.11.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"concurrently": "^9.0.1",
|
||||
|
|
@ -1154,23 +1153,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz",
|
||||
"integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
|
|
@ -1184,16 +1166,6 @@
|
|||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/alpinejs": {
|
||||
"version": "3.15.12",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz",
|
||||
"integrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "~3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"alpinejs": "^3.15.12",
|
||||
"axios": "^1.11.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"concurrently": "^9.0.1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 40" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7c3aed"/>
|
||||
<stop offset="100%" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<rect x="22" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<path d="M5.5 7.5L22 32.5" stroke="url(#g)" stroke-width="5.5" stroke-linecap="round"/>
|
||||
<text x="36" y="29" font-family="Inter, ui-sans-serif, sans-serif" font-weight="700" font-size="22" letter-spacing="-0.5" fill="#f0f0f5">nimuli</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 660 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 40" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7c3aed"/>
|
||||
<stop offset="100%" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<rect x="22" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<path d="M5.5 7.5L22 32.5" stroke="url(#g)" stroke-width="5.5" stroke-linecap="round"/>
|
||||
<text x="36" y="29" font-family="Inter, ui-sans-serif, sans-serif" font-weight="700" font-size="22" letter-spacing="-0.5" fill="#111827">nimuli</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 660 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7c3aed"/>
|
||||
<stop offset="100%" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- n-shaped mark: two vertical bars connected by diagonal -->
|
||||
<rect x="4" y="6" width="5" height="20" rx="2.5" fill="url(#g)"/>
|
||||
<rect x="23" y="6" width="5" height="20" rx="2.5" fill="url(#g)"/>
|
||||
<path d="M9 8.5L23 23.5" stroke="url(#g)" stroke-width="5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 562 B |
|
|
@ -0,0 +1,14 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 40" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7c3aed"/>
|
||||
<stop offset="100%" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- Mark -->
|
||||
<rect x="0" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<rect x="22" y="5" width="5.5" height="30" rx="2.75" fill="url(#g)"/>
|
||||
<path d="M5.5 7.5L22 32.5" stroke="url(#g)" stroke-width="5.5" stroke-linecap="round"/>
|
||||
<!-- Wordmark: nimuli -->
|
||||
<text x="36" y="29" font-family="Inter, ui-sans-serif, sans-serif" font-weight="700" font-size="22" letter-spacing="-0.5" fill="url(#g)">nimuli</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 704 B |
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7c3aed"/>
|
||||
<stop offset="100%" stop-color="#ec4899"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="4" y="6" width="5" height="20" rx="2.5" fill="url(#g)"/>
|
||||
<rect x="23" y="6" width="5" height="20" rx="2.5" fill="url(#g)"/>
|
||||
<path d="M9 8.5L23 23.5" stroke="url(#g)" stroke-width="5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 496 B |
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Nimuli",
|
||||
"short_name": "Nimuli",
|
||||
"description": "Short links, QR codes, and bio pages — all in one place.",
|
||||
"start_url": "/dashboard",
|
||||
"display": "standalone",
|
||||
"background_color": "#0a0a0f",
|
||||
"theme_color": "#7c3aed",
|
||||
"icons": [
|
||||
{ "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
|
||||
]
|
||||
}
|
||||
|
|
@ -6,19 +6,26 @@
|
|||
@source '../**/*.js';
|
||||
|
||||
@theme {
|
||||
/* Dark mode (default) */
|
||||
--color-bg: #0a0a0f;
|
||||
--color-s1: #111117;
|
||||
--color-s2: #16161e;
|
||||
--color-s3: #1c1c26;
|
||||
--color-b1: rgb(255 255 255 / 6%);
|
||||
--color-b2: rgb(255 255 255 / 10%);
|
||||
--color-b1: rgb(255 255 255 / 8%);
|
||||
--color-b2: rgb(255 255 255 / 14%);
|
||||
--color-t1: #f0f0f5;
|
||||
--color-t2: #8888a0;
|
||||
--color-t3: #44445a;
|
||||
--color-blue: #4f8ef7;
|
||||
|
||||
/* Accent — purple-pink gradient, consistent across both themes */
|
||||
--color-blue: #7c3aed;
|
||||
--color-accent: #7c3aed;
|
||||
--color-accent-2: #ec4899;
|
||||
|
||||
--color-green: #3ecf8e;
|
||||
--color-amber: #f5a623;
|
||||
--color-red: #f05252;
|
||||
|
||||
--font-mono: 'JetBrains Mono', monospace;
|
||||
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
|
@ -33,8 +40,30 @@
|
|||
--color-t1: #111827;
|
||||
--color-t2: #6b7280;
|
||||
--color-t3: #9ca3af;
|
||||
|
||||
--color-blue: #7c3aed;
|
||||
--color-accent: #7c3aed;
|
||||
--color-accent-2: #ec4899;
|
||||
|
||||
--color-green: #10b981;
|
||||
--color-amber: #f59e0b;
|
||||
--color-red: #ef4444;
|
||||
}
|
||||
|
||||
/* Visible borders on all themes */
|
||||
*, ::before, ::after { border-color: var(--color-b1); }
|
||||
|
||||
@layer utilities {
|
||||
.bg-accent-gradient {
|
||||
background: linear-gradient(135deg, var(--color-accent), var(--color-accent-2));
|
||||
}
|
||||
.text-accent-gradient {
|
||||
background: linear-gradient(135deg, var(--color-accent), var(--color-accent-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.border-accent-gradient {
|
||||
border-image: linear-gradient(135deg, var(--color-accent), var(--color-accent-2)) 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,23 @@
|
|||
import '../css/app.css';
|
||||
import './bootstrap';
|
||||
import Alpine from 'alpinejs';
|
||||
import Chart from 'chart.js/auto';
|
||||
window.Chart = Chart;
|
||||
|
||||
// Theme store — reads localStorage, applies to <html data-theme>
|
||||
// Alpine stores registered via alpine:init — Livewire v3 owns Alpine lifecycle
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('theme', {
|
||||
current: localStorage.getItem('nimuli_theme') || document.documentElement.dataset.theme || 'dark',
|
||||
|
||||
init() {
|
||||
this.apply(this.current);
|
||||
},
|
||||
|
||||
init() { this.apply(this.current); },
|
||||
apply(theme) {
|
||||
this.current = theme;
|
||||
document.documentElement.dataset.theme = theme;
|
||||
localStorage.setItem('nimuli_theme', theme);
|
||||
},
|
||||
|
||||
cycle() {
|
||||
const order = ['dark', 'light', 'system'];
|
||||
const next = order[(order.indexOf(this.current) + 1) % order.length];
|
||||
this.apply(next);
|
||||
this.apply(order[(order.indexOf(this.current) + 1) % order.length]);
|
||||
},
|
||||
});
|
||||
|
||||
// Sidebar drawer store — mobile open/close
|
||||
Alpine.store('sidebar', {
|
||||
open: false,
|
||||
Alpine.store('sidebar', { open: false });
|
||||
});
|
||||
|
||||
window.Alpine = Alpine;
|
||||
Alpine.start();
|
||||
|
|
|
|||
|
|
@ -100,8 +100,9 @@
|
|||
@click="wsOpen = !wsOpen"
|
||||
class="w-full flex items-center gap-3 px-4 py-4 hover:bg-s2 transition-colors border-b border-white/[.06]"
|
||||
>
|
||||
<img src="/brand/logo-mark.svg" alt="Nimuli" class="w-7 h-7 flex-shrink-0">
|
||||
<div class="flex-1 text-left min-w-0">
|
||||
<div class="text-sm font-bold text-t1 tracking-tight">nimuli.</div>
|
||||
<div class="text-sm font-bold tracking-tight text-accent-gradient">nimuli</div>
|
||||
@if($workspace)
|
||||
<div class="text-xs text-t2 truncate mt-0.5">{{ $workspace->name }}</div>
|
||||
@else
|
||||
|
|
|
|||
|
|
@ -6,11 +6,26 @@
|
|||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ ($title ?? '') ? $title . ' — Nimuli' : 'Nimuli' }}</title>
|
||||
|
||||
<!-- Prevent FOUC: inline critical theme vars -->
|
||||
<style>
|
||||
html { background: #0a0a0f; color: #f0f0f5; }
|
||||
[data-theme="light"] { background: #ffffff; color: #111827; }
|
||||
</style>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<meta name="theme-color" content="#0a0a0f" media="(prefers-color-scheme: dark)">
|
||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
||||
|
||||
@vite(['resources/js/app.js'])
|
||||
@livewireStyles
|
||||
</head>
|
||||
|
|
@ -30,13 +45,15 @@
|
|||
aria-hidden="true"
|
||||
></div>
|
||||
|
||||
<!-- Sidebar: always visible on md+, drawer on mobile -->
|
||||
<!-- Sidebar: persisted across wire:navigate to prevent re-render -->
|
||||
@persist('sidebar')
|
||||
<div
|
||||
class="fixed inset-y-0 left-0 z-30 md:relative md:z-auto md:flex md:flex-shrink-0 transition-transform duration-200 ease-in-out"
|
||||
:class="$store.sidebar?.open ? 'translate-x-0' : '-translate-x-full md:translate-x-0'"
|
||||
>
|
||||
<x-sidebar />
|
||||
</div>
|
||||
@endpersist
|
||||
|
||||
<!-- Main Area -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden min-w-0">
|
||||
|
|
|
|||
Loading…
Reference in New Issue