+
+
+
diff --git a/frontend/src/components/ui/EmptyState.vue b/frontend/src/components/ui/EmptyState.vue
new file mode 100644
index 0000000..4b88b78
--- /dev/null
+++ b/frontend/src/components/ui/EmptyState.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
{{ title }}
+
{{ description }}
+
+
+
+
+
+
+
diff --git a/frontend/src/components/ui/PageHeading.vue b/frontend/src/components/ui/PageHeading.vue
new file mode 100644
index 0000000..a94a913
--- /dev/null
+++ b/frontend/src/components/ui/PageHeading.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+ {{ eyebrow }}
+
+
{{ title }}
+
{{ subtitle }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/ui/SectionHeader.vue b/frontend/src/components/ui/SectionHeader.vue
new file mode 100644
index 0000000..e2006d6
--- /dev/null
+++ b/frontend/src/components/ui/SectionHeader.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/components/ui/StatusDot.vue b/frontend/src/components/ui/StatusDot.vue
new file mode 100644
index 0000000..a507a33
--- /dev/null
+++ b/frontend/src/components/ui/StatusDot.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
diff --git a/frontend/src/components/ui/ToastContainer.vue b/frontend/src/components/ui/ToastContainer.vue
index 6e68191..a7a1e3f 100644
--- a/frontend/src/components/ui/ToastContainer.vue
+++ b/frontend/src/components/ui/ToastContainer.vue
@@ -7,18 +7,18 @@ const { toasts, remove } = useToast()
const typeConfig: Record
= {
success: {
icon: CheckCircle,
- color: '#22c55e',
- bg: 'rgba(34, 197, 94, 0.10)',
+ color: 'var(--st-work)',
+ bg: 'rgba(61, 220, 151, 0.10)',
},
error: {
icon: XCircle,
- color: '#ef4444',
- bg: 'rgba(239, 68, 68, 0.10)',
+ color: 'var(--st-block)',
+ bg: 'rgba(251, 113, 133, 0.10)',
},
info: {
icon: Info,
- color: '#3b82f6',
- bg: 'rgba(59, 130, 246, 0.10)',
+ color: 'var(--a-blue)',
+ bg: 'rgba(79, 124, 255, 0.10)',
},
}
@@ -76,7 +76,7 @@ const typeConfig: Record = {
0 8px 32px rgba(0, 0, 0, 0.4),
inset 0 1px 0 color-mix(in srgb, var(--toast-color) 12%, transparent);
pointer-events: auto;
- color: #e8eaf0;
+ color: var(--tx);
font-size: 12.5px;
line-height: 1.4;
}
@@ -106,7 +106,7 @@ const typeConfig: Record = {
border: none;
border-radius: 6px;
background: transparent;
- color: #6b7385;
+ color: var(--tx-3);
cursor: pointer;
opacity: 0.5;
transition: all 0.15s;
@@ -114,7 +114,7 @@ const typeConfig: Record = {
.toast-close:hover {
opacity: 1;
background: rgba(255, 255, 255, 0.06);
- color: #e8eaf0;
+ color: var(--tx);
}
/* Transition animations */
diff --git a/frontend/src/components/ui/button/index.ts b/frontend/src/components/ui/button/index.ts
index d54ea4e..9c4dd22 100644
--- a/frontend/src/components/ui/button/index.ts
+++ b/frontend/src/components/ui/button/index.ts
@@ -4,25 +4,35 @@ import { type VariantProps, cva } from 'class-variance-authority'
export { default as Button } from './Button.vue'
export const buttonVariants = cva(
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
+ // shadcn-original
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
- destructive:
- 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
- outline:
- 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
- secondary:
- 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
+ destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
+ outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
+ secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
+
+ // Nexus V2 — Token-basierte Varianten
+ /** CTA / primäre Aktion — Gradient */
+ gradient: 'nexus-btn-gradient',
+ /** Icon-Only — quadratischer Button ohne Text */
+ icon: 'nexus-btn-icon',
+ /** Ghost thin — minimal hover */
+ ghostSubtle: 'nexus-btn-ghost-subtle',
+ /** Danger/Blocker — rote Akzent-Aktion */
+ danger: 'nexus-btn-danger',
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
+ iconSm: 'h-7 w-7 rounded-md',
+ pill: 'h-7 px-4 rounded-full text-xs',
},
},
defaultVariants: {
diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts
new file mode 100644
index 0000000..c1a787d
--- /dev/null
+++ b/frontend/src/components/ui/index.ts
@@ -0,0 +1,13 @@
+export { default as Badge } from './Badge.vue'
+export { default as Card } from './Card.vue'
+export { default as StatusDot } from './StatusDot.vue'
+export { default as PageHeading } from './PageHeading.vue'
+export { default as EmptyState } from './EmptyState.vue'
+export { default as ActivityTimeline } from './ActivityTimeline.vue'
+export { default as SectionHeader } from './SectionHeader.vue'
+export { default as Input } from './Input.vue'
+export { default as Textarea } from './Textarea.vue'
+export { default as Select } from './Select.vue'
+export { default as Dialog } from './Dialog.vue'
+export { default as ToastContainer } from './ToastContainer.vue'
+export { Button } from './button'