feat: updated mobile responsiveness, refactored sidebar #1

Merged
Nicholai merged 1 commits from UI-enhancements into main 2026-01-14 22:36:18 +00:00
8 changed files with 405 additions and 188 deletions
Showing only changes of commit cb71d2563f - Show all commits

96
App.tsx
View File

@ -16,47 +16,75 @@ const App: React.FC = () => {
useLayoutEffect(() => {
const ctx = gsap.context(() => {
// Hero Animation
const tl = gsap.timeline();
tl.fromTo(titleRef.current,
{ y: 50, opacity: 0, rotate: 2 },
{ y: 0, opacity: 1, rotate: 0, duration: 1, ease: "power3.out" }
)
.fromTo(subtitleRef.current,
{ y: 20, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.8, ease: "power2.out" },
"-=0.5"
);
const mm = gsap.matchMedia();
// Section Entry Animations
const sections = gsap.utils.toArray<HTMLElement>('.guide-section');
sections.forEach(section => {
gsap.fromTo(section,
{ opacity: 0, y: 50 },
{
opacity: 1,
y: 0,
duration: 0.8,
ease: "power2.out",
scrollTrigger: {
trigger: section,
start: "top 80%",
toggleActions: "play none none reverse"
}
}
// Desktop Animations
mm.add("(min-width: 768px)", () => {
// Hero Animation
const tl = gsap.timeline();
tl.fromTo(titleRef.current,
{ y: 50, opacity: 0, rotate: 2 },
{ y: 0, opacity: 1, rotate: 0, duration: 1, ease: "power3.out" }
)
.fromTo(subtitleRef.current,
{ y: 20, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.8, ease: "power2.out" },
"-=0.5"
);
// Section Entry Animations
const sections = gsap.utils.toArray<HTMLElement>('.guide-section');
sections.forEach(section => {
gsap.fromTo(section,
{ opacity: 0, y: 50 },
{
opacity: 1,
y: 0,
duration: 0.8,
ease: "power2.out",
scrollTrigger: {
trigger: section,
start: "top 80%",
toggleActions: "play none none reverse"
}
}
);
});
});
// Mobile Animations (Simplified)
mm.add("(max-width: 767px)", () => {
gsap.set([titleRef.current, subtitleRef.current], { opacity: 1, y: 0, rotate: 0 });
const sections = gsap.utils.toArray<HTMLElement>('.guide-section');
sections.forEach(section => {
gsap.fromTo(section,
{ opacity: 0 },
{
opacity: 1,
duration: 0.5,
ease: "power1.out",
scrollTrigger: {
trigger: section,
start: "top 90%",
toggleActions: "play none none reverse"
}
}
);
});
});
}, mainRef);
return () => ctx.revert();
}, []);
return (
<div className="flex min-h-screen bg-background text-foreground font-sans selection:bg-primary/20 selection:text-primary">
<div className="flex min-h-screen bg-background text-foreground font-sans selection:bg-primary/20 selection:text-primary overflow-x-hidden">
<Sidebar />
<main ref={mainRef} className="flex-1 lg:ml-72 w-full">
<main ref={mainRef} className="flex-1 lg:ml-64 w-full transition-all duration-300">
{/* Hero Section */}
<div ref={heroRef} className="min-h-[80vh] flex flex-col justify-center px-8 md:px-16 lg:px-24 bg-card relative overflow-hidden">
{/* Subtle blurred blobs */}
@ -90,14 +118,14 @@ const App: React.FC = () => {
</div>
{/* Content Sections */}
<div className="px-6 md:px-16 lg:px-24 py-12 max-w-5xl mx-auto pb-32">
<div className="px-6 md:px-16 lg:px-24 py-20 max-w-5xl mx-auto pb-32">
{GUIDE_CONTENT.map((section, index) => (
<section
key={section.id}
id={section.id}
className="guide-section mb-24 scroll-mt-24"
className="guide-section mb-32 scroll-mt-24"
>
<div className="flex items-center gap-4 mb-8">
<div className="flex items-center gap-4 mb-10">
<span className="flex items-center justify-center w-10 h-10 rounded-full bg-foreground text-background font-mono font-bold text-lg shadow-lg">
{index + 1}
</span>
@ -111,7 +139,7 @@ const App: React.FC = () => {
</div>
{index < GUIDE_CONTENT.length - 1 && (
<div className="h-px w-full bg-gradient-to-r from-transparent via-border to-transparent mt-16"></div>
<div className="h-px w-full bg-gradient-to-r from-transparent via-border to-transparent mt-24"></div>
)}
</section>
))}

View File

@ -15,34 +15,23 @@ const Callout: React.FC<{ variant?: string; content: string }> = ({ variant = 'i
case 'warning': return <AlertTriangle className="text-destructive" />;
case 'tip': return <Lightbulb className="text-accent" />;
case 'success': return <CheckCircle2 className="text-primary" />;
default: return <Info className="text-blue-500" />;
default: return <Info className="text-primary" />;
}
};
const getStyles = () => {
switch(variant) {
case 'warning': return 'bg-destructive/10 border-destructive/20 text-destructive-foreground';
case 'tip': return 'bg-accent/10 border-accent/20 text-accent-foreground';
case 'success': return 'bg-primary/10 border-primary/20 text-primary-foreground';
default: return 'bg-blue-50 border-blue-200 text-blue-900';
case 'warning': return 'bg-destructive/10 border-destructive/20 text-foreground';
case 'tip': return 'bg-accent/10 border-accent/20 text-foreground';
case 'success': return 'bg-primary/10 border-primary/20 text-foreground';
default: return 'bg-secondary border-secondary-foreground/10 text-secondary-foreground';
}
};
// Adjusting styles to match new variables more closely for text colors where simple bg/text classes might fail due to variable usage
// The tailwind classes using vars (like text-primary) work if defined in config, which we did.
const getContainerClass = () => {
switch(variant) {
case 'warning': return 'bg-red-50 border-red-200 text-red-900';
case 'tip': return 'bg-amber-50 border-amber-200 text-amber-900';
case 'success': return 'bg-green-50 border-green-200 text-green-900';
default: return 'bg-blue-50 border-blue-200 text-blue-900';
}
}
return (
<div className={`flex gap-4 p-4 my-4 rounded-lg border-l-4 ${getContainerClass()}`}>
<div className={`flex gap-4 p-4 my-4 rounded-lg border-l-4 ${getStyles()}`}>
<div className="shrink-0 pt-0.5">{getIcon()}</div>
<p className="text-sm md:text-base">{content}</p>
<p className="text-base">{content}</p>
</div>
);
};

View File

@ -68,11 +68,11 @@ export const GitWorkflowDiagram: React.FC = () => {
<p className="text-xs text-muted-foreground mb-8">Where you edit files.</p>
{state === 'modified' && (
<div ref={fileRef} className="bg-white p-3 rounded shadow-md border border-gray-200 flex items-center gap-3">
<FileText className="text-orange-500" />
<div ref={fileRef} className="bg-card p-3 rounded shadow-md border border-border flex items-center gap-3">
<FileText className="text-destructive" />
<div>
<div className="text-sm font-bold">script.js</div>
<div className="text-xs text-orange-600">Modified</div>
<div className="text-base font-bold">script.js</div>
<div className="text-sm text-destructive">Modified</div>
</div>
</div>
)}
@ -82,7 +82,7 @@ export const GitWorkflowDiagram: React.FC = () => {
disabled={state !== 'modified'}
onClick={() => setState('staged')}
className={`
px-4 py-2 rounded-full text-xs font-bold transition-all
px-4 py-2 rounded-full text-sm font-bold transition-all
${state === 'modified'
? 'bg-primary text-primary-foreground hover:bg-primary/90 hover:scale-105 shadow-md cursor-pointer'
: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'
@ -96,18 +96,18 @@ export const GitWorkflowDiagram: React.FC = () => {
{/* Staging Area Zone */}
<div ref={stagingRef} className={`relative p-4 rounded-lg border-2 transition-colors duration-300 ${state === 'staged' ? 'border-primary bg-primary/5' : 'border-dashed border-muted'}`}>
<div className="flex items-center gap-2 mb-4 text-sm font-semibold text-foreground/80">
<div className="flex items-center gap-2 mb-4 text-base font-semibold text-foreground/80">
<Layers size={18} />
<span>Staging Area</span>
</div>
<p className="text-xs text-muted-foreground mb-8">Files ready to commit.</p>
<p className="text-sm text-muted-foreground mb-8">Files ready to commit.</p>
{state === 'staged' && (
<div ref={fileRef} className="bg-white p-3 rounded shadow-md border border-gray-200 flex items-center gap-3">
<FileText className="text-green-500" />
<div ref={fileRef} className="bg-card p-3 rounded shadow-md border border-border flex items-center gap-3">
<FileText className="text-secondary" />
<div>
<div className="text-sm font-bold">script.js</div>
<div className="text-xs text-green-600">Staged</div>
<div className="text-base font-bold">script.js</div>
<div className="text-sm text-secondary">Staged</div>
</div>
</div>
)}
@ -117,7 +117,7 @@ export const GitWorkflowDiagram: React.FC = () => {
disabled={state !== 'staged'}
onClick={() => setState('committed')}
className={`
px-4 py-2 rounded-full text-xs font-bold transition-all
px-4 py-2 rounded-full text-sm font-bold transition-all
${state === 'staged'
? 'bg-primary text-primary-foreground hover:bg-primary/90 hover:scale-105 shadow-md cursor-pointer'
: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'
@ -131,18 +131,18 @@ export const GitWorkflowDiagram: React.FC = () => {
{/* Repository Zone */}
<div ref={repoRef} className={`relative p-4 rounded-lg border-2 transition-colors duration-300 ${state === 'committed' ? 'border-primary bg-primary/5' : 'border-dashed border-muted'}`}>
<div className="flex items-center gap-2 mb-4 text-sm font-semibold text-foreground/80">
<div className="flex items-center gap-2 mb-4 text-base font-semibold text-foreground/80">
<Database size={18} />
<span>Repository</span>
</div>
<p className="text-xs text-muted-foreground mb-8">Saved history.</p>
<p className="text-sm text-muted-foreground mb-8">Saved history.</p>
{state === 'committed' && (
<div ref={fileRef} className="bg-white p-3 rounded shadow-md border border-gray-200 flex items-center gap-3">
<FileText className="text-blue-500" />
<div ref={fileRef} className="bg-card p-3 rounded shadow-md border border-border flex items-center gap-3">
<FileText className="text-primary" />
<div>
<div className="text-sm font-bold">script.js</div>
<div className="text-xs text-blue-600">Committed</div>
<div className="text-base font-bold">script.js</div>
<div className="text-sm text-primary">Committed</div>
</div>
</div>
)}
@ -151,7 +151,7 @@ export const GitWorkflowDiagram: React.FC = () => {
<button
disabled={state !== 'committed'}
className={`
px-4 py-2 rounded-full text-xs font-bold transition-all
px-4 py-2 rounded-full text-sm font-bold transition-all
${state === 'committed'
? 'bg-secondary text-secondary-foreground cursor-default'
: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { GUIDE_CONTENT } from '../constants';
import { Leaf, Menu, X, GitBranch } from 'lucide-react';
import { Leaf, Menu, X, GitBranch, ArrowRight } from 'lucide-react';
export const Sidebar: React.FC = () => {
const [activeId, setActiveId] = useState<string>('');
@ -36,60 +36,141 @@ export const Sidebar: React.FC = () => {
return (
<>
{/* Mobile Toggle */}
{/* Mobile Toggle & Drawer (unchanged logic, adjusted styling for mobile-only) */}
<div className="fixed top-4 right-4 z-50 lg:hidden">
<button
onClick={() => setIsMobileOpen(!isMobileOpen)}
className="p-2 bg-card rounded-full shadow-lg border border-border text-foreground"
className="p-3 bg-card rounded-full shadow-lg border border-border text-foreground hover:bg-muted transition-colors min-w-[44px] min-h-[44px] flex items-center justify-center"
aria-label="Toggle Menu"
>
{isMobileOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Sidebar Container */}
{isMobileOpen && (
<div
className="fixed inset-0 bg-background/80 backdrop-blur-sm z-40 lg:hidden animate-in fade-in duration-200"
onClick={() => setIsMobileOpen(false)}
/>
)}
{/* Mobile Sidebar Content */}
<nav className={`
fixed top-0 left-0 h-full bg-card/95 backdrop-blur-md border-r border-border
w-72 transform transition-transform duration-300 z-40
fixed top-0 left-0 h-full bg-card border-r border-border
w-[85vw] max-w-xs transform transition-transform duration-300 z-50 shadow-2xl lg:hidden flex flex-col
${isMobileOpen ? 'translate-x-0' : '-translate-x-full'}
lg:translate-x-0
`}>
<div className="p-6 h-full flex flex-col">
<div className="flex items-center gap-3 mb-10 group cursor-default">
<div className="p-2 bg-primary/10 rounded-lg group-hover:bg-primary/20 transition-colors">
<Leaf className="text-primary" size={24} />
<div className="p-6 pb-2">
<div className="flex items-center gap-3 mb-8">
<div className="p-2 bg-primary/10 rounded-lg">
<Leaf className="text-primary" size={20} />
</div>
<div>
<h1 className="font-bold text-foreground text-lg leading-tight">Git for Sylvi</h1>
<h1 className="font-bold text-foreground text-lg leading-tight">Git Guide</h1>
<span className="text-xs text-muted-foreground font-mono">v1.0.0</span>
</div>
</div>
</div>
<div className="flex-1 overflow-y-auto pr-2 custom-scrollbar">
<div className="space-y-1">
{GUIDE_CONTENT.map((section) => (
<button
key={section.id}
onClick={() => scrollToSection(section.id)}
className={`
w-full text-left px-4 py-3 rounded-md text-sm transition-all duration-200
flex items-center gap-3 group
${activeId === section.id
? 'bg-primary/10 text-primary font-semibold shadow-sm'
: 'text-muted-foreground hover:bg-muted/50 hover:text-foreground'
}
`}
>
<span className={`w-1.5 h-1.5 rounded-full transition-colors ${activeId === section.id ? 'bg-primary' : 'bg-muted-foreground/30 group-hover:bg-muted-foreground'}`}></span>
<span className="truncate">{section.title.split('. ')[1]}</span>
</button>
))}
<div className="flex-1 overflow-y-auto px-4 custom-scrollbar">
<ul className="space-y-1 pb-8">
{GUIDE_CONTENT.map((section) => {
const isActive = activeId === section.id;
return (
<li key={section.id}>
<button
onClick={() => scrollToSection(section.id)}
className={`
w-full text-left px-4 py-3 rounded-md text-base transition-all duration-200
flex items-center justify-between
${isActive
? 'bg-primary/10 text-foreground font-bold'
: 'text-muted-foreground font-medium active:bg-muted/50'
}
`}
>
<span className="truncate">{section.title.split('. ')[1]}</span>
{isActive && (
<span className="w-1.5 h-1.5 rounded-full bg-primary shrink-0"></span>
)}
</button>
</li>
);
})}
</ul>
</div>
<div className="p-6 border-t border-border/50 bg-card">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<GitBranch size={16} />
<span>Happy Coding! 🌱</span>
</div>
</div>
</nav>
{/* NEW: Minimal Desktop Navigation */}
<nav className="hidden lg:block fixed top-0 left-0 h-full w-64 pl-6 pr-4" aria-label="Desktop Navigation">
{/* Positioning container to match the 'mt-[187px]' feel, centered vertically or with specific top padding */}
<div className="h-full flex flex-col justify-center max-h-[calc(100vh-40px)] overflow-y-auto overflow-x-hidden custom-scrollbar pb-10">
{/* Header/Logo Area for Desktop */}
<div className="mb-6 px-3 group cursor-default">
<div className="flex items-center gap-3 opacity-70 group-hover:opacity-100 transition-opacity">
<Leaf className="text-primary" size={18} />
<span className="font-bold text-foreground tracking-tight text-sm">Git Guide</span>
</div>
</div>
<div className="mt-6 pt-6 border-t border-border">
<div className="flex items-center gap-3 text-sm text-muted-foreground">
<GitBranch size={16} />
<span>Happy Coding! 🌱</span>
<ul className="space-y-0.5">
{GUIDE_CONTENT.map((section) => {
const isActive = activeId === section.id;
return (
<li key={section.id} className="group relative">
{/* Hover/Active Pill Background */}
<div
className={`
absolute left-0 top-0 z-[-1] h-full w-full rounded-md transition-all duration-200 ease-linear
${isActive ? 'bg-primary/10 opacity-100' : 'bg-primary/5 opacity-0 group-hover:opacity-100'}
`}
/>
<button
onClick={() => scrollToSection(section.id)}
className={`
relative block w-full text-left px-3 py-1.5 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20 rounded-md
${isActive
? 'text-foreground font-bold'
: 'text-muted-foreground font-medium hover:text-foreground'
}
`}
>
<span className="flex items-center justify-between text-sm">
<span>{section.title.split('. ')[1]}</span>
{/* Minimal Arrow on Hover/Active */}
<span
className={`
transition-all duration-200 transform
${isActive
? 'opacity-100 translate-x-0 text-primary'
: 'opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 text-muted-foreground'
}
`}
>
<ArrowRight size={12} strokeWidth={2.5} />
</span>
</span>
</button>
</li>
);
})}
</ul>
{/* Footer Area */}
<div className="mt-8 px-3 pt-4 border-t border-border/50">
<div className="flex items-center gap-2 text-[10px] text-muted-foreground hover:text-primary transition-colors cursor-pointer group">
<GitBranch size={12} className="group-hover:rotate-12 transition-transform" />
<span>v1.0.0</span>
</div>
</div>
</div>

View File

@ -66,7 +66,7 @@ export const TerminalBlock: React.FC<TerminalBlockProps> = ({ code, language = '
<div
ref={containerRef}
// Using OKLCH based on the primary hue (142.77) for a perfectly matched "dark forest" look
className="my-10 rounded-[2rem] overflow-hidden border border-primary/20 shadow-2xl relative group bg-[oklch(0.18_0.02_142.77)]"
className="my-6 md:my-10 rounded-[2rem] overflow-hidden border border-primary/20 shadow-2xl relative group bg-[oklch(0.18_0.02_142.77)]"
style={{
boxShadow: '0 20px 50px -12px oklch(0.15 0.05 142.77 / 0.5)'
}}
@ -88,7 +88,7 @@ export const TerminalBlock: React.FC<TerminalBlockProps> = ({ code, language = '
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-full bg-white/5 border border-white/10">
<Sprout size={14} className="text-primary" />
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-foreground/70">
<span className="text-xs font-bold uppercase tracking-[0.2em] text-primary-foreground/70">
{language}
</span>
</div>
@ -109,12 +109,12 @@ export const TerminalBlock: React.FC<TerminalBlockProps> = ({ code, language = '
{copied ? (
<>
<Check size={14} className="stroke-[3px]" />
<span className="text-[10px] font-black uppercase">Copied!</span>
<span className="text-xs font-black uppercase">Copied!</span>
</>
) : (
<>
<Copy size={14} className="group-hover/btn:rotate-12 transition-transform" />
<span className="text-[10px] font-black uppercase">Copy</span>
<span className="text-xs font-black uppercase">Copy</span>
</>
)}
</div>

191
index.css Normal file
View File

@ -0,0 +1,191 @@
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:wght@400;500;600&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Sora:wght@400;600;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: oklch(0.9818 0.0054 95.0986);
}
::-webkit-scrollbar-thumb {
background: oklch(0.8847 0.0069 97.3627);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}
body {
background-color: var(--background);
color: var(--foreground);
overflow-x: hidden;
font-family: var(--font-sans);
}
:root {
--background: oklch(0.9818 0.0054 95.0986);
--foreground: oklch(0.3438 0.0269 95.7226);
--card: oklch(0.9818 0.0054 95.0986);
--card-foreground: oklch(0.2174 0.0019 106.5582);
--popover: oklch(1.0000 0 0);
--popover-foreground: oklch(0.2671 0.0196 98.9390);
--primary: oklch(0.6551 0.0198 142.7714);
--primary-foreground: oklch(0.9688 0.0066 106.5223);
--secondary: oklch(0.9245 0.0138 92.9892);
--secondary-foreground: oklch(0.3821 0.0407 166.3004);
--muted: oklch(0.9369 0.0124 91.5218);
--muted-foreground: oklch(0.4866 0.0160 145.3235);
--accent: oklch(0.8097 0.0177 145.4098);
--accent-foreground: oklch(0.2891 0 0);
--destructive: oklch(0.6507 0.1836 39.0189);
--destructive-foreground: oklch(0.8429 0.1511 88.3005);
--border: oklch(0.8847 0.0069 97.3627);
--input: oklch(0.7621 0.0156 98.3528);
--ring: oklch(0.6507 0.1836 39.0189);
--chart-1: oklch(0.6768 0.0517 143.1617);
--chart-2: oklch(0.3821 0.0407 166.3004);
--chart-3: oklch(0.8429 0.1511 88.3005);
--chart-4: oklch(0.6507 0.1836 39.0189);
--chart-5: oklch(0.3972 0.0698 227.1914);
--sidebar: oklch(0.6551 0.0198 142.7714);
--sidebar-foreground: oklch(0.9688 0.0066 106.5223);
--sidebar-primary: oklch(0.3821 0.0407 166.3004);
--sidebar-primary-foreground: oklch(0.9881 0 0);
--sidebar-accent: oklch(0.6121 0.0189 145.3393);
--sidebar-accent-foreground: oklch(1.0000 0 0);
--sidebar-border: oklch(0 0 0);
--sidebar-ring: oklch(0 0 0);
--font-sans: Sora, ui-sans-serif, sans-serif, system-ui;
--font-serif: IBM Plex Serif, ui-serif, serif;
--font-mono: Google Sans Code, ui-monospace, monospace;
--radius: 0.275rem;
--shadow-x: 0px;
--shadow-y: 0px;
--shadow-blur: 8px;
--shadow-spread: -4px;
--shadow-opacity: 0.79;
--shadow-color: #000000;
--shadow-2xs: 0px 0px 8px -4px hsl(0 0% 0% / 0.40);
--shadow-xs: 0px 0px 8px -4px hsl(0 0% 0% / 0.40);
--shadow-sm: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 1px 2px -5px hsl(0 0% 0% / 0.79);
--shadow: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 1px 2px -5px hsl(0 0% 0% / 0.79);
--shadow-md: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 2px 4px -5px hsl(0 0% 0% / 0.79);
--shadow-lg: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 4px 6px -5px hsl(0 0% 0% / 0.79);
--shadow-xl: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 8px 10px -5px hsl(0 0% 0% / 0.79);
--shadow-2xl: 0px 0px 8px -4px hsl(0 0% 0% / 1.98);
--tracking-normal: 0em;
--spacing: 0.25rem;
}
.dark {
--background: oklch(0.2178 0 0);
--foreground: oklch(0.8074 0.0142 93.0137);
--card: oklch(0.2679 0.0036 106.6427);
--card-foreground: oklch(0.9818 0.0054 95.0986);
--popover: oklch(0.3085 0.0035 106.6039);
--popover-foreground: oklch(0.9211 0.0040 106.4781);
--primary: oklch(0.6551 0.0198 142.7714);
--primary-foreground: oklch(0.9688 0.0066 106.5223);
--secondary: oklch(0.9245 0.0138 92.9892);
--secondary-foreground: oklch(0.3821 0.0407 166.3004);
--muted: oklch(0.2561 0.0071 145.3653);
--muted-foreground: oklch(0.7713 0.0169 99.0657);
--accent: oklch(0.4890 0.0180 145.2933);
--accent-foreground: oklch(0.9663 0.0080 98.8792);
--destructive: oklch(0.6507 0.1836 39.0189);
--destructive-foreground: oklch(1.0000 0 0);
--border: oklch(0.2809 0 0);
--input: oklch(0.6551 0.0198 142.7714);
--ring: oklch(0.6507 0.1836 39.0189);
--chart-1: oklch(0.6768 0.0517 143.1617);
--chart-2: oklch(0.6979 0.0567 64.8386);
--chart-3: oklch(0.8429 0.1511 88.3005);
--chart-4: oklch(0.6507 0.1836 39.0189);
--chart-5: oklch(0.3972 0.0698 227.1914);
--sidebar: oklch(0.3740 0.0150 145.2682);
--sidebar-foreground: oklch(0.8074 0.0142 93.0137);
--sidebar-primary: oklch(0.3250 0 0);
--sidebar-primary-foreground: oklch(0.9881 0 0);
--sidebar-accent: oklch(0.5451 0.0175 145.3296);
--sidebar-accent-foreground: oklch(1.0000 0 0);
--sidebar-border: oklch(0.9401 0 0);
--sidebar-ring: oklch(0.7731 0 0);
--font-sans: Sora, ui-sans-serif, sans-serif, system-ui;
--font-serif: IBM Plex Serif, ui-serif, serif;
--font-mono: Google Sans Code, ui-monospace, monospace;
--radius: 0.275rem;
--shadow-x: 0px;
--shadow-y: 0px;
--shadow-blur: 8px;
--shadow-spread: -4px;
--shadow-opacity: 0.79;
--shadow-color: oklch(0 0 0);
--shadow-2xs: 0px 0px 8px -4px hsl(0 0% 0% / 0.40);
--shadow-xs: 0px 0px 8px -4px hsl(0 0% 0% / 0.40);
--shadow-sm: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 1px 2px -5px hsl(0 0% 0% / 0.79);
--shadow: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 1px 2px -5px hsl(0 0% 0% / 0.79);
--shadow-md: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 2px 4px -5px hsl(0 0% 0% / 0.79);
--shadow-lg: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 4px 6px -5px hsl(0 0% 0% / 0.79);
--shadow-xl: 0px 0px 8px -4px hsl(0 0% 0% / 0.79), 0px 8px 10px -5px hsl(0 0% 0% / 0.79);
--shadow-2xl: 0px 0px 8px -4px hsl(0 0% 0% / 1.98);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-serif: var(--font-serif);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--shadow-2xs: var(--shadow-2xs);
--shadow-xs: var(--shadow-xs);
--shadow-sm: var(--shadow-sm);
--shadow: var(--shadow);
--shadow-md: var(--shadow-md);
--shadow-lg: var(--shadow-lg);
--shadow-xl: var(--shadow-xl);
--shadow-2xl: var(--shadow-2xl);
}

View File

@ -15,7 +15,11 @@
extend: {
fontFamily: {
sans: ['var(--font-sans)', 'Sora', 'Inter', 'sans-serif'],
mono: ['var(--font-mono)', 'JetBrains Mono', 'monospace'],
serif: ['var(--font-serif)', 'IBM Plex Serif', 'serif'],
mono: ['var(--font-mono)', 'Google Sans Mono', 'JetBrains Mono', 'monospace'],
},
borderRadius: {
DEFAULT: 'var(--radius)',
},
colors: {
background: 'var(--background)',
@ -57,82 +61,6 @@
}
}
</script>
<style>
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: oklch(0.9818 0.0054 95.0986);
}
::-webkit-scrollbar-thumb {
background: oklch(0.8847 0.0069 97.3627);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}
body {
background-color: var(--background);
color: var(--foreground);
overflow-x: hidden;
font-family: var(--font-sans);
}
:root {
--background: oklch(0.9818 0.0054 95.0986);
--foreground: oklch(0.3438 0.0269 95.7226);
--card: oklch(0.9818 0.0054 95.0986);
--card-foreground: oklch(0.2174 0.0019 106.5582);
--popover: oklch(1.0000 0 0);
--popover-foreground: oklch(0.2671 0.0196 98.9390);
--primary: oklch(0.6551 0.0198 142.7714);
--primary-foreground: oklch(0.9688 0.0066 106.5223);
--secondary: oklch(0.9245 0.0138 92.9892);
--secondary-foreground: oklch(0.3821 0.0407 166.3004);
--muted: oklch(0.9369 0.0124 91.5218);
--muted-foreground: oklch(0.4866 0.0160 145.3235);
--accent: oklch(0.8097 0.0177 145.4098);
--accent-foreground: oklch(0.2891 0 0);
--destructive: oklch(0.6507 0.1836 39.0189);
--destructive-foreground: oklch(0.8429 0.1511 88.3005);
--border: oklch(0.8847 0.0069 97.3627);
--input: oklch(0.7621 0.0156 98.3528);
--ring: oklch(0.6507 0.1836 39.0189);
--font-sans: 'Sora', 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--radius: 0.5rem;
}
.grain-texture {
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
opacity: 0.05;
pointer-events: none;
}
.dark {
--background: oklch(0.2178 0 0);
--foreground: oklch(0.8074 0.0142 93.0137);
--card: oklch(0.2679 0.0036 106.6427);
--card-foreground: oklch(0.9818 0.0054 95.0986);
--popover: oklch(0.3085 0.0035 106.6039);
--popover-foreground: oklch(0.9211 0.0040 106.4781);
--primary: oklch(0.6551 0.0198 142.7714);
--primary-foreground: oklch(0.9688 0.0066 106.5223);
--secondary: oklch(0.9245 0.0138 92.9892);
--secondary-foreground: oklch(0.3821 0.0407 166.3004);
--muted: oklch(0.2561 0.0071 145.3653);
--muted-foreground: oklch(0.7713 0.0169 99.0657);
--accent: oklch(0.4890 0.0180 145.2933);
--accent-foreground: oklch(0.9663 0.0080 98.8792);
--destructive: oklch(0.6507 0.1836 39.0189);
--destructive-foreground: oklch(1.0000 0 0);
--border: oklch(0.2809 0 0);
--input: oklch(0.6551 0.0198 142.7714);
--ring: oklch(0.6507 0.1836 39.0189);
}
</style>
<script type="importmap">
{
"imports": {
@ -145,7 +73,6 @@
}
}
</script>
<link rel="stylesheet" href="/index.css">
</head>
<body>
<div id="root"></div>

View File

@ -1,6 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
const rootElement = document.getElementById('root');
if (!rootElement) {