Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,18 @@ body {
}

.glass-panel {
background: rgba(255, 255, 255, 0.015);
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: var(--panel-shadow);
border-radius: 1.25rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: var(--shadow-md);
border-radius: var(--radius-panel);
}

.glass-card {
background: rgba(8, 8, 12, 0.85);
background: color-mix(in srgb, var(--card) 86%, transparent);
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: var(--shadow-soft);
box-shadow: var(--shadow-lg);
backdrop-filter: blur(18px);
border-radius: 1.5rem;
border-radius: var(--radius-card);
}

.panel-muted {
Expand All @@ -119,11 +119,11 @@ body {
}

.lab-hero {
border-radius: 32px;
border-radius: var(--radius-panel);
border: 1px solid rgba(255, 255, 255, 0.08);
background: linear-gradient(145deg, rgba(255, 255, 255, 0.04), rgba(0, 0, 0, 0.55));
background: var(--mk-surface-glow), linear-gradient(145deg, rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.55));
padding: 1.75rem;
box-shadow: var(--panel-shadow);
box-shadow: var(--shadow-lg);
}

.lab-grid {
Expand All @@ -138,11 +138,11 @@ body {
}

.context-panel {
border-radius: 28px;
border-radius: var(--radius-panel);
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(8, 8, 12, 0.7);
background: color-mix(in srgb, var(--mk-bg-panel) 85%, transparent);
padding: 1.25rem;
box-shadow: var(--shadow-soft);
box-shadow: var(--shadow-md);
}

@keyframes mkGradientShift {
Expand Down Expand Up @@ -173,6 +173,22 @@ body {
transform: translate(-50%, 1rem);
}

[data-icon],
.icon-base {
width: var(--icon-size);
height: var(--icon-size);
stroke-width: var(--icon-stroke);
flex-shrink: 0;
}

.icon-sm {
--icon-size: var(--icon-size-sm);
}

.icon-gap {
gap: var(--icon-gap);
}

.section-heading {
font-size: clamp(1.25rem, 2.2vw, 2rem);
font-weight: 600;
Expand Down
58 changes: 36 additions & 22 deletions app/theme.css
Original file line number Diff line number Diff line change
@@ -1,51 +1,65 @@
:root,
.theme-dark {
color-scheme: dark;
--mk-bg: #020203;
--mk-bg-panel: #08080a;
--mk-bg-panel-muted: #101017;
--mk-bg-card: #12121f;
--mk-border: #252537;
--mk-border-soft: #191927;
--mk-text: #ffffff;
--mk-text-muted: rgba(255, 255, 255, 0.68);
--mk-bg: #06060b;
--mk-bg-surface: #0b0b12;
--mk-bg-panel: #151827;
--mk-bg-panel-muted: #1b1f33;
--mk-bg-card: #111424;
--mk-border: #222536;
--mk-border-strong: #2e3246;
--mk-border-soft: #1a1d2b;
--mk-text: #f7f8fb;
--mk-text-muted: rgba(247, 248, 251, 0.72);
--mk-accent: #ff6ec7;
--mk-accent-2: #ff3cac;
--mk-accent-3: #8b5cf6;
--mk-success: #5ef7b7;
--mk-warning: #ffc857;
--mk-danger: #ff6b6b;
--mk-gradient: linear-gradient(90deg, #ff6ec7, #ff3cac, #8b5cf6);
--mk-success: #6ee7b7;
--mk-warning: #f2cc60;
--mk-danger: #ff7878;
--mk-gradient: linear-gradient(115deg, #ff6ec7, #ff3cac, #8b5cf6);
--mk-surface-glow: linear-gradient(145deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0));
--background: var(--mk-bg);
--foreground: var(--mk-text);
--card: var(--mk-bg-card);
--card-foreground: var(--mk-text);
--popover: #050508;
--popover: #07070f;
--popover-foreground: var(--mk-text);
--primary: var(--mk-accent);
--primary-foreground: #080208;
--secondary: var(--mk-accent-3);
--secondary-foreground: #080208;
--secondary-foreground: #0b0611;
--muted: var(--mk-bg-panel-muted);
--muted-foreground: var(--mk-text-muted);
--accent: var(--mk-accent);
--accent-foreground: #080208;
--accent-foreground: #0c0a14;
--destructive: var(--mk-danger);
--destructive-foreground: #050202;
--destructive-foreground: #120102;
--border: var(--mk-border);
--input: var(--mk-border-soft);
--ring: var(--mk-accent-2);
--radius: 28px;
--sidebar: rgba(2, 2, 4, 0.94);
--sidebar-foreground: rgba(255, 255, 255, 0.65);
--radius: 18px;
--radius-control: 14px;
--radius-card: 22px;
--radius-panel: 24px;
--sidebar: rgba(6, 6, 11, 0.95);
--sidebar-foreground: rgba(247, 248, 251, 0.8);
--sidebar-primary: rgba(255, 110, 199, 0.12);
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: rgba(255, 255, 255, 0.03);
--sidebar-accent: rgba(247, 248, 251, 0.04);
--sidebar-accent-foreground: var(--mk-text);
--sidebar-border: rgba(255, 255, 255, 0.08);
--sidebar-border: rgba(255, 255, 255, 0.07);
--sidebar-ring: var(--mk-accent);
--shadow-sm: 0 10px 24px -18px rgba(0, 0, 0, 0.58);
--shadow-md: 0 18px 42px -20px rgba(0, 0, 0, 0.6);
--shadow-lg: 0 30px 64px -24px rgba(0, 0, 0, 0.62);
--shadow-focus: 0 0 0 3px rgba(255, 110, 199, 0.28);
--shadow-soft: 0 20px 60px rgba(0, 0, 0, 0.55);
--panel-shadow: 0 25px 80px rgba(0, 0, 0, 0.75);
--panel-shadow: 0 26px 70px rgba(0, 0, 0, 0.7);
--icon-size: 20px;
--icon-size-sm: 16px;
--icon-stroke: 1.6;
--icon-gap: 10px;
}

body.theme-dark {
Expand Down
88 changes: 55 additions & 33 deletions components/discover/AdminDiscoverEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { useEffect, useMemo, useState } from 'react';
import useSWR from 'swr';
import { Loader2, Plus, RefreshCcw, Save, Trash2 } from 'lucide-react';
import type { DiscoverCommunityHighlight, DiscoverEvent, DiscoverFeed, DiscoverQuestHighlight } from '@mk/api-client';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Skeleton } from '@/components/ui/skeleton';

const fetcher = (url: string) => fetch(url).then((response) => {
if (!response.ok) {
Expand Down Expand Up @@ -118,48 +120,67 @@ export function AdminDiscoverEditor() {

if (isLoading || !feed) {
return (
<section className="glass-card rounded-3xl p-6 md:p-8">
<div className="flex items-center gap-3 text-white">
<Loader2 className="h-5 w-5 animate-spin" />
<p>Loading Discover feed…</p>
<section className="glass-card rounded-[var(--radius-card)] p-6 md:p-8">
<div className="space-y-6 text-foreground">
<div className="flex items-center icon-gap text-muted-foreground">
<Loader2 className="icon-base animate-spin" />
<p className="text-sm">Loading Discover feed…</p>
</div>
<div className="grid gap-4 md:grid-cols-2">
{[0, 1, 2, 3].map((item) => (
<div
key={item}
className="rounded-[var(--radius-control)] border border-border/50 bg-muted/30 p-4"
>
<Skeleton className="h-4 w-24 rounded-sm bg-muted-foreground/20" />
<Skeleton className="mt-3 h-6 w-16 rounded-sm bg-muted-foreground/20" />
</div>
))}
</div>
<div className="space-y-3">
{[0, 1, 2].map((row) => (
<Skeleton
key={row}
className="h-11 rounded-[var(--radius-control)] bg-muted/30"
/>
))}
</div>
</div>
</section>
);
}

if (error) {
return (
<section className="glass-card rounded-3xl border border-destructive/40 p-6 text-red-100">
<section className="glass-card rounded-[var(--radius-card)] border border-destructive/40 p-6 text-red-100">
<p>Unable to load Discover feed. Please refresh.</p>
</section>
);
}

return (
<div className="space-y-6">
<section className="glass-card rounded-3xl p-6 md:p-8 text-white">
<section className="glass-card rounded-[var(--radius-card)] p-6 md:p-8 text-white">
<div className="flex flex-wrap items-center gap-4">
<button
<Button
type="button"
className={cn(
'inline-flex items-center gap-2 rounded-full bg-primary px-4 py-2 text-sm font-semibold text-white shadow-lg transition hover:brightness-110',
isSaving && 'opacity-70'
)}
className="rounded-full px-6"
onClick={handleSave}
disabled={isSaving || !draft}
>
<Save className="h-4 w-4" />
<Save className="icon-sm" />
{isSaving ? 'Saving…' : 'Save feed'}
</button>
<button
</Button>
<Button
type="button"
className="inline-flex items-center gap-2 rounded-full border border-white/20 px-4 py-2 text-sm text-white/90 transition hover:bg-white/10"
variant="outline"
className="rounded-full px-6"
onClick={resetDraft}
disabled={!data}
>
<RefreshCcw className="h-4 w-4" />
<RefreshCcw className="icon-sm" />
Reset
</button>
</Button>
{toast ? <p className="text-sm text-emerald-200">{toast}</p> : null}
</div>
{totals ? (
Expand Down Expand Up @@ -294,34 +315,35 @@ type SectionEditorProps<TItem> = {

function SectionEditor<TItem>({ title, description, items, onAdd, onRemove, render }: SectionEditorProps<TItem>) {
return (
<section className="glass-card rounded-3xl p-6 md:p-8 text-white">
<section className="glass-card rounded-[var(--radius-card)] p-6 md:p-8 text-white">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div>
<h2 className="text-2xl font-semibold">{title}</h2>
<p className="text-sm text-slate-200">{description}</p>
</div>
<button
type="button"
onClick={onAdd}
className="inline-flex items-center gap-2 rounded-full border border-white/20 px-4 py-2 text-sm text-white/90 transition hover:bg-white/10"
>
<Plus className="h-4 w-4" />
<Button type="button" onClick={onAdd} variant="outline" className="rounded-full" size="sm">
<Plus className="icon-sm" />
Add entry
</button>
</Button>
</div>

<div className="mt-6 space-y-4">
{items.map((item, index) => (
<article key={index} className="rounded-2xl border border-white/10 bg-white/5 p-4">
<article
key={index}
className="rounded-[var(--radius-card)] border border-border/50 bg-muted/30 p-4"
>
<div className="flex items-center justify-between">
<p className="text-sm font-semibold text-white/80">Entry {index + 1}</p>
<button
<Button
type="button"
variant="ghost"
size="sm"
className="text-xs text-muted-foreground hover:text-destructive"
onClick={() => onRemove(index)}
className="text-xs text-white/70 transition hover:text-red-300"
>
<Trash2 className="mr-1 inline h-4 w-4" /> Remove
</button>
<Trash2 className="icon-sm" /> Remove
</Button>
</div>
<div className="mt-4 space-y-4 text-sm text-slate-200">{render(item, index)}</div>
</article>
Expand All @@ -344,8 +366,8 @@ function InputField({ label, value, onChange }: InputFieldProps) {
return (
<label className="block text-sm">
<span className="text-xs uppercase tracking-wide text-white/70">{label}</span>
<input
className="mt-1 w-full rounded-xl border border-white/10 bg-white/10 px-3 py-2 text-white outline-none focus-visible:border-primary"
<Input
className="mt-1 border-border/60 bg-transparent"
value={value}
onChange={(event) => onChange(event.target.value)}
/>
Expand Down
4 changes: 2 additions & 2 deletions components/shell/SidebarNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ export function SidebarNav({ isOpen, onNavigate }: SidebarNavProps) {
key={item.id}
href={href}
className={cn(
"group flex items-center gap-3 rounded-2xl px-3 py-3 text-sm font-semibold transition justify-center xl:justify-start",
"group icon-gap flex items-center rounded-2xl px-3 py-3 text-sm font-semibold transition justify-center xl:justify-start",
active
? "bg-sidebar-primary text-sidebar-primary-foreground shadow-lg"
: "bg-transparent text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
)}
onClick={onNavigate}
aria-current={active ? "page" : undefined}
>
<Icon className="h-5 w-5" aria-hidden="true" />
<Icon className="icon-base" aria-hidden="true" />
<span className="hidden xl:inline">{t(item.id)}</span>
</Link>
);
Expand Down
17 changes: 8 additions & 9 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-2xl border border-transparent text-sm font-semibold tracking-tight transition-all duration-200 ease-out disabled:pointer-events-none disabled:opacity-60 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive hover:-translate-y-0.5 active:translate-y-0",
"inline-flex items-center justify-center gap-[var(--icon-gap)] whitespace-nowrap rounded-[var(--radius-control)] border border-transparent text-sm font-semibold leading-tight tracking-tight shadow-[var(--shadow-sm)] transition-all duration-200 ease-out disabled:pointer-events-none disabled:opacity-60 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-[var(--icon-size)] [&_svg]:stroke-[var(--icon-stroke)] [&_svg]:shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:shadow-[var(--shadow-focus)] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive hover:-translate-y-0.5 active:translate-y-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow-[0_0_20px_-6px_var(--primary)] hover:bg-primary/85 hover:shadow-[0_0_35px_-8px_var(--primary)]",
default: "bg-primary text-primary-foreground shadow-[var(--shadow-md)] hover:bg-primary/85 hover:shadow-[var(--shadow-lg)]",
destructive:
"bg-destructive text-white shadow-[0_0_25px_-8px_var(--destructive)] hover:bg-destructive/90 focus-visible:ring-destructive/30 dark:focus-visible:ring-destructive/40 dark:bg-destructive/80",
"bg-destructive text-white shadow-[var(--shadow-md)] hover:bg-destructive/90 focus-visible:ring-destructive/30 dark:focus-visible:ring-destructive/40 dark:bg-destructive/80",
outline:
"border-border/40 bg-background/80 shadow-[0_15px_35px_-25px_rgba(0,0,0,0.65)] hover:border-primary/40 hover:bg-primary/5 hover:text-primary",
"border-border/50 bg-background/80 shadow-[var(--shadow-sm)] hover:border-primary/50 hover:bg-primary/5 hover:text-primary",
secondary:
"bg-secondary text-secondary-foreground shadow-[0_0_20px_-6px_var(--secondary)] hover:bg-secondary/80 hover:shadow-[0_0_35px_-8px_var(--secondary)]",
ghost:
"hover:bg-primary/5 hover:text-primary",
"bg-secondary text-secondary-foreground shadow-[var(--shadow-md)] hover:bg-secondary/80 hover:shadow-[var(--shadow-lg)]",
ghost: "hover:bg-primary/5 hover:text-primary shadow-none",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-12 px-7 py-2.5 has-[>svg]:px-5",
sm: "h-10 rounded-xl gap-1.5 px-4 has-[>svg]:px-3 text-sm",
lg: "h-14 rounded-3xl px-9 has-[>svg]:px-7 text-base",
sm: "h-10 rounded-[calc(var(--radius-control)-2px)] gap-[calc(var(--icon-gap)-2px)] px-4 has-[>svg]:px-3 text-sm",
lg: "h-14 rounded-[calc(var(--radius-control)+4px)] px-9 has-[>svg]:px-7 text-base",
icon: "size-12",
"icon-sm": "size-10",
"icon-lg": "size-14",
Expand Down
2 changes: 1 addition & 1 deletion components/ui/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"group relative flex flex-col gap-6 overflow-hidden rounded-[1.75rem] border border-border/40 bg-gradient-to-br from-card/85 via-card/60 to-muted/25 p-8 text-card-foreground shadow-[0_25px_45px_-30px_rgba(0,0,0,0.65)] transition-all duration-300 ease-out hover:-translate-y-1 hover:shadow-[0_35px_60px_-28px_rgba(0,0,0,0.7)] backdrop-blur-md",
"group relative flex flex-col gap-6 overflow-hidden rounded-[var(--radius-card)] border border-border/50 bg-[linear-gradient(140deg,color-mix(in_srgb,var(--card)_88%,transparent),color-mix(in_srgb,var(--muted)_24%,transparent))] p-8 text-card-foreground shadow-[var(--shadow-md)] transition-all duration-300 ease-out hover:-translate-y-1 hover:shadow-[var(--shadow-lg)] backdrop-blur-md",
className
)}
{...props}
Expand Down
4 changes: 2 additions & 2 deletions components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input/80 h-11 w-full min-w-0 rounded-[var(--radius-control)] border bg-[color-mix(in_srgb,var(--muted)_35%,transparent)] px-4 py-2 text-base shadow-[var(--shadow-sm)] transition-[color,box-shadow,border] outline-none file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:shadow-[var(--shadow-focus)]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
)}
Expand Down
Loading