feat(v2): design tokens, fonts (Manrope/Space Grotesk/JetBrains Mono), galaxy background
This commit is contained in:
@@ -5,6 +5,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#080a0f" />
|
<meta name="theme-color" content="#080a0f" />
|
||||||
<title>Nexus | Noveria Operations</title>
|
<title>Nexus | Noveria Operations</title>
|
||||||
|
<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=JetBrains+Mono:wght@400;600;700&family=Manrope:wght@400;500;600;700;800&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -4,6 +4,52 @@
|
|||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
/* ── Nexus V2 Theme (Tailwind v4 @theme directive) ── */
|
||||||
|
@theme {
|
||||||
|
/* Font families */
|
||||||
|
--font-sans: 'Manrope', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
--font-display: 'Space Grotesk', sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
|
||||||
|
/* Space surfaces */
|
||||||
|
--color-space-0: #050410;
|
||||||
|
--color-space-1: #0a0818;
|
||||||
|
--color-space-2: #0e0c20;
|
||||||
|
--color-space-3: #141130;
|
||||||
|
--color-space-4: #1b1742;
|
||||||
|
|
||||||
|
/* Glass */
|
||||||
|
--color-glass: rgba(20, 17, 48, 0.55);
|
||||||
|
--color-glass-2: rgba(28, 24, 64, 0.55);
|
||||||
|
|
||||||
|
/* Lines */
|
||||||
|
--color-line: rgba(150, 140, 255, 0.10);
|
||||||
|
--color-line-2: rgba(150, 140, 255, 0.18);
|
||||||
|
--color-line-3: rgba(150, 140, 255, 0.30);
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
--color-tx: #ece9ff;
|
||||||
|
--color-tx-2: #a8a3d6;
|
||||||
|
--color-tx-3: #6f6aa0;
|
||||||
|
|
||||||
|
/* Accent */
|
||||||
|
--color-a-blue: #4f7cff;
|
||||||
|
--color-a-purple: #b557f6;
|
||||||
|
--color-a-mid: #7c6cff;
|
||||||
|
|
||||||
|
/* Status */
|
||||||
|
--color-st-work: #3ddc97;
|
||||||
|
--color-st-think: #34d6f5;
|
||||||
|
--color-st-queue: #fbbf24;
|
||||||
|
--color-st-block: #fb7185;
|
||||||
|
--color-st-idle: #6b6796;
|
||||||
|
|
||||||
|
/* Border radius */
|
||||||
|
--radius-r: 14px;
|
||||||
|
--radius-r-sm: 10px;
|
||||||
|
--radius-r-lg: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 210 40% 98%;
|
||||||
@@ -39,7 +85,7 @@
|
|||||||
body {
|
body {
|
||||||
background: hsl(var(--background));
|
background: hsl(var(--background));
|
||||||
color: hsl(var(--foreground));
|
color: hsl(var(--foreground));
|
||||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
font-family: 'Manrope', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||||
'Segoe UI', sans-serif;
|
'Segoe UI', sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/* ================================================================
|
||||||
|
nexus-tokens.css — Nexus Mission Control V2 Design Tokens
|
||||||
|
Geladen NACH main.css, überschreibt shadcn/v1-Standards.
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* ── Surfaces ─────────────────────────────────────── */
|
||||||
|
--space-0: #050410;
|
||||||
|
--space-1: #0a0818;
|
||||||
|
--space-2: #0e0c20;
|
||||||
|
--space-3: #141130;
|
||||||
|
--space-4: #1b1742;
|
||||||
|
--glass: rgba(20, 17, 48, 0.55);
|
||||||
|
--glass-2: rgba(28, 24, 64, 0.55);
|
||||||
|
|
||||||
|
/* ── Lines ────────────────────────────────────────── */
|
||||||
|
--line: rgba(150, 140, 255, 0.10);
|
||||||
|
--line-2: rgba(150, 140, 255, 0.18);
|
||||||
|
--line-3: rgba(150, 140, 255, 0.30);
|
||||||
|
|
||||||
|
/* ── Text ─────────────────────────────────────────── */
|
||||||
|
--tx: #ece9ff;
|
||||||
|
--tx-2: #a8a3d6;
|
||||||
|
--tx-3: #6f6aa0;
|
||||||
|
|
||||||
|
/* ── Accent Gradient ──────────────────────────────── */
|
||||||
|
--a-blue: #4f7cff;
|
||||||
|
--a-purple: #b557f6;
|
||||||
|
--a-mid: #7c6cff;
|
||||||
|
--grad: linear-gradient(120deg, var(--a-blue), var(--a-purple));
|
||||||
|
--grad-soft: linear-gradient(120deg, rgba(79,124,255,.18), rgba(181,87,246,.18));
|
||||||
|
|
||||||
|
/* ── Status ───────────────────────────────────────── */
|
||||||
|
--st-work: #3ddc97;
|
||||||
|
--st-think: #34d6f5;
|
||||||
|
--st-queue: #fbbf24;
|
||||||
|
--st-block: #fb7185;
|
||||||
|
--st-idle: #6b6796;
|
||||||
|
|
||||||
|
/* ── Glows ────────────────────────────────────────── */
|
||||||
|
--glow: 0 0 0 1px rgba(124,108,255,.20), 0 0 28px -4px rgba(124,108,255,.55);
|
||||||
|
--glow-blue: 0 0 24px -2px rgba(79,124,255,.65);
|
||||||
|
--glow-purple: 0 0 24px -2px rgba(181,87,246,.60);
|
||||||
|
--glow-work: 0 0 16px -1px rgba(61,220,151,.70);
|
||||||
|
--glow-think: 0 0 16px -1px rgba(52,214,245,.65);
|
||||||
|
|
||||||
|
/* ── Radius ───────────────────────────────────────── */
|
||||||
|
--r: 14px;
|
||||||
|
--r-sm: 10px;
|
||||||
|
--r-lg: 20px;
|
||||||
|
|
||||||
|
/* ── Layout ───────────────────────────────────────── */
|
||||||
|
--sidebar-w: 248px;
|
||||||
|
--topbar-h: 62px;
|
||||||
|
--rail-w: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Glass card utility ────────────────────────────── */
|
||||||
|
.glass-panel {
|
||||||
|
background: var(--glass);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: var(--r);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Gradient text ─────────────────────────────────── */
|
||||||
|
.grad-text {
|
||||||
|
background: var(--grad);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Status dot keyframes ──────────────────────────── */
|
||||||
|
@keyframes pulse-work {
|
||||||
|
0% { box-shadow: 0 0 0 0 rgba(61,220,151,.55); }
|
||||||
|
70% { box-shadow: 0 0 0 7px rgba(61,220,151,0); }
|
||||||
|
100% { box-shadow: 0 0 0 0 rgba(61,220,151,0); }
|
||||||
|
}
|
||||||
|
@keyframes pulse-think {
|
||||||
|
0% { box-shadow: 0 0 0 0 rgba(52,214,245,.55); }
|
||||||
|
70% { box-shadow: 0 0 0 7px rgba(52,214,245,0); }
|
||||||
|
100% { box-shadow: 0 0 0 0 rgba(52,214,245,0); }
|
||||||
|
}
|
||||||
|
@keyframes pulse-block {
|
||||||
|
0% { box-shadow: 0 0 0 0 rgba(251,113,133,.55); }
|
||||||
|
70% { box-shadow: 0 0 0 7px rgba(251,113,133,0); }
|
||||||
|
100% { box-shadow: 0 0 0 0 rgba(251,113,133,0); }
|
||||||
|
}
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { transform: translateX(-100%); }
|
||||||
|
100% { transform: translateX(180%); }
|
||||||
|
}
|
||||||
|
/* ── V2 Scrollbars ─────────────────────────────────── */
|
||||||
|
.v2-scroll::-webkit-scrollbar { width: 9px; height: 9px; }
|
||||||
|
.v2-scroll::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(124,108,255,.22);
|
||||||
|
border-radius: 9px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
.v2-scroll::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(124,108,255,.4);
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
.v2-scroll::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
|
||||||
|
/* ── Typography helpers ────────────────────────────── */
|
||||||
|
.font-display { font-family: 'Space Grotesk', sans-serif; }
|
||||||
|
.font-mono-v2 { font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* GalaxyBackground – Canvas Starfield + Aurora Blobs
|
||||||
|
*
|
||||||
|
* Ported from assets/galaxy.js (design_handoff_nexus_v2).
|
||||||
|
* Auto-mounts onMounted; cleans up onUnmounted.
|
||||||
|
* Fixed overlay with z-index:0 and pointer-events:none.
|
||||||
|
*/
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const rootRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
interface Star {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
r: number
|
||||||
|
a: number
|
||||||
|
tw: number
|
||||||
|
ph: number
|
||||||
|
hue: number
|
||||||
|
dx: number
|
||||||
|
dy: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Shoot {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
len: number
|
||||||
|
sp: number
|
||||||
|
ang: number
|
||||||
|
life: number
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizeObserver: ResizeObserver | null = null
|
||||||
|
let animFrameId = 0
|
||||||
|
let canvas: HTMLCanvasElement | null = null
|
||||||
|
let ctx: CanvasRenderingContext2D | null = null
|
||||||
|
|
||||||
|
function mount(root: HTMLElement) {
|
||||||
|
canvas = document.createElement('canvas')
|
||||||
|
root.appendChild(canvas)
|
||||||
|
ctx = canvas.getContext('2d')
|
||||||
|
if (!ctx) return
|
||||||
|
|
||||||
|
const dpr = Math.min(window.devicePixelRatio || 1, 2)
|
||||||
|
let stars: Star[] = []
|
||||||
|
let shoots: Shoot[] = []
|
||||||
|
let W = 0
|
||||||
|
let H = 0
|
||||||
|
let t = 0
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
const r = root.getBoundingClientRect()
|
||||||
|
W = r.width
|
||||||
|
H = r.height
|
||||||
|
canvas!.width = W * dpr
|
||||||
|
canvas!.height = H * dpr
|
||||||
|
ctx!.setTransform(dpr, 0, 0, dpr, 0, 0)
|
||||||
|
|
||||||
|
const count = Math.round((W * H) / 5200)
|
||||||
|
stars = []
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
stars.push({
|
||||||
|
x: Math.random() * W,
|
||||||
|
y: Math.random() * H,
|
||||||
|
r: Math.random() * 1.3 + 0.25,
|
||||||
|
a: Math.random() * 0.6 + 0.2,
|
||||||
|
tw: Math.random() * 0.025 + 0.004,
|
||||||
|
ph: Math.random() * Math.PI * 2,
|
||||||
|
hue: Math.random() < 0.5 ? 230 : 270,
|
||||||
|
dx: (Math.random() - 0.5) * 0.04,
|
||||||
|
dy: (Math.random() - 0.5) * 0.04,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawnShoot() {
|
||||||
|
const fromLeft = Math.random() < 0.6
|
||||||
|
shoots.push({
|
||||||
|
x: fromLeft ? -40 : W * (0.4 + Math.random() * 0.5),
|
||||||
|
y: Math.random() * H * 0.5,
|
||||||
|
len: 90 + Math.random() * 120,
|
||||||
|
sp: 6 + Math.random() * 5,
|
||||||
|
ang: Math.random() * 0.3 + 0.15,
|
||||||
|
life: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function frame() {
|
||||||
|
ctx!.clearRect(0, 0, W, H)
|
||||||
|
t += 1
|
||||||
|
|
||||||
|
// Draw stars
|
||||||
|
for (let i = 0; i < stars.length; i++) {
|
||||||
|
const s = stars[i]
|
||||||
|
s.ph += s.tw
|
||||||
|
const a = s.a * (0.55 + 0.45 * Math.sin(s.ph))
|
||||||
|
s.x += s.dx
|
||||||
|
s.y += s.dy
|
||||||
|
if (s.x < 0) s.x = W
|
||||||
|
if (s.x > W) s.x = 0
|
||||||
|
if (s.y < 0) s.y = H
|
||||||
|
if (s.y > H) s.y = 0
|
||||||
|
|
||||||
|
ctx!.beginPath()
|
||||||
|
ctx!.fillStyle = `hsla(${s.hue},90%,82%,${a})`
|
||||||
|
ctx!.arc(s.x, s.y, s.r, 0, Math.PI * 2)
|
||||||
|
ctx!.fill()
|
||||||
|
|
||||||
|
// Glow for larger stars
|
||||||
|
if (s.r > 1) {
|
||||||
|
ctx!.beginPath()
|
||||||
|
ctx!.fillStyle = `hsla(${s.hue},95%,80%,${a * 0.12})`
|
||||||
|
ctx!.arc(s.x, s.y, s.r * 3.5, 0, Math.PI * 2)
|
||||||
|
ctx!.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shooting stars
|
||||||
|
if (Math.random() < 0.004 && shoots.length < 2) spawnShoot()
|
||||||
|
for (let j = shoots.length - 1; j >= 0; j--) {
|
||||||
|
const sh = shoots[j]
|
||||||
|
sh.x += Math.cos(sh.ang) * sh.sp
|
||||||
|
sh.y += Math.sin(sh.ang) * sh.sp
|
||||||
|
sh.life -= 0.006
|
||||||
|
|
||||||
|
const ex = sh.x - Math.cos(sh.ang) * sh.len
|
||||||
|
const ey = sh.y - Math.sin(sh.ang) * sh.len
|
||||||
|
const g = ctx!.createLinearGradient(sh.x, sh.y, ex, ey)
|
||||||
|
g.addColorStop(0, `rgba(200,210,255,${0.9 * sh.life})`)
|
||||||
|
g.addColorStop(1, 'rgba(160,120,255,0)')
|
||||||
|
|
||||||
|
ctx!.strokeStyle = g
|
||||||
|
ctx!.lineWidth = 2
|
||||||
|
ctx!.lineCap = 'round'
|
||||||
|
ctx!.beginPath()
|
||||||
|
ctx!.moveTo(sh.x, sh.y)
|
||||||
|
ctx!.lineTo(ex, ey)
|
||||||
|
ctx!.stroke()
|
||||||
|
|
||||||
|
if (sh.life <= 0 || sh.x > W + 60 || sh.y > H + 60) shoots.splice(j, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
animFrameId = requestAnimationFrame(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeObserver = new ResizeObserver(() => resize())
|
||||||
|
resizeObserver.observe(root)
|
||||||
|
resize()
|
||||||
|
frame()
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect()
|
||||||
|
resizeObserver = null
|
||||||
|
}
|
||||||
|
if (animFrameId) {
|
||||||
|
cancelAnimationFrame(animFrameId)
|
||||||
|
animFrameId = 0
|
||||||
|
}
|
||||||
|
if (canvas && canvas.parentNode) {
|
||||||
|
canvas.parentNode.removeChild(canvas)
|
||||||
|
}
|
||||||
|
canvas = null
|
||||||
|
ctx = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (rootRef.value) mount(rootRef.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
cleanup()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="rootRef" class="galaxy-bg">
|
||||||
|
<div class="aurora a1"></div>
|
||||||
|
<div class="aurora a2"></div>
|
||||||
|
<div class="aurora a3"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.galaxy-bg {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
radial-gradient(1200px 800px at 18% -8%, rgba(79,124,255,.20), transparent 60%),
|
||||||
|
radial-gradient(1000px 760px at 92% 8%, rgba(181,87,246,.18), transparent 60%),
|
||||||
|
radial-gradient(900px 700px at 60% 110%, rgba(70,60,180,.20), transparent 60%),
|
||||||
|
linear-gradient(180deg, #070512, #0a0818 60%, #060410);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.galaxy-bg canvas {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.aurora {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(70px);
|
||||||
|
opacity: 0.5;
|
||||||
|
mix-blend-mode: screen;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
.a1 {
|
||||||
|
width: 46vw;
|
||||||
|
height: 46vw;
|
||||||
|
left: -8vw;
|
||||||
|
top: -14vw;
|
||||||
|
background: radial-gradient(circle, rgba(79,124,255,.55), transparent 65%);
|
||||||
|
animation: drift1 26s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.a2 {
|
||||||
|
width: 40vw;
|
||||||
|
height: 40vw;
|
||||||
|
right: -10vw;
|
||||||
|
top: 2vw;
|
||||||
|
background: radial-gradient(circle, rgba(181,87,246,.5), transparent 65%);
|
||||||
|
animation: drift2 32s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.a3 {
|
||||||
|
width: 42vw;
|
||||||
|
height: 42vw;
|
||||||
|
left: 34vw;
|
||||||
|
bottom: -18vw;
|
||||||
|
background: radial-gradient(circle, rgba(95,80,220,.45), transparent 65%);
|
||||||
|
animation: drift3 30s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes drift1 {
|
||||||
|
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||||
|
50% { transform: translate(8vw, 6vw) scale(1.12); }
|
||||||
|
}
|
||||||
|
@keyframes drift2 {
|
||||||
|
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||||
|
50% { transform: translate(-7vw, 5vw) scale(1.1); }
|
||||||
|
}
|
||||||
|
@keyframes drift3 {
|
||||||
|
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||||
|
50% { transform: translate(4vw, -6vw) scale(1.15); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,6 +4,7 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import { useAuthStore } from './stores/auth'
|
import { useAuthStore } from './stores/auth'
|
||||||
import './assets/main.css'
|
import './assets/main.css'
|
||||||
|
import './assets/nexus-tokens.css'
|
||||||
|
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user