Skip to content
Open
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
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,25 @@ Open → http://localhost:3000
```
StudyPlan
├── css
│ └── index.css # Contains all styling rules, variables, and animations
│ ├── index.css # Entry point — imports layered stylesheets
│ ├── tokens.css # Design tokens (color, spacing, type, motion)
│ ├── base.css # Reset, typography, a11y utilities
│ ├── layout.css # Header, footer, app grid, breakpoints
│ ├── animations.css # Keyframes, stagger utilities
│ ├── components.css # Buttons, forms, modals, toggles (BEM)
│ ├── features.css # App-specific UI (calendar, tasks, panel, focus)
│ └── effects.css # Glass surfaces, gradients, micro-interactions
├── js
│ ├── ui
│ │ └── templates.js # Reusable HTML templates for tasks, forms, extract cards
│ ├── utils
│ │ ├── aiMock.js # The original mock UI extraction hook (deprecated)
│ │ └── api.js # The live fetch logic communicating with our Express API
│ ├── app.js # The main controller (handles DOM UI, event bindings, and Calendar)
│ └── store.js # The Custom State Manager handling our frontend Pub/Sub state
│ │ ├── api.js # The live fetch logic communicating with our Express API
│ │ └── dom.js # DOM helpers (escape, modals, toast, date format)
│ ├── app.js # Main controller (views, calendar, tasks, panel)
│ ├── auth.js # Login / signup modal logic
│ ├── settings.js # Theme and compact-view preferences
│ └── store.js # Custom state manager (pub/sub)
├── .env.example # Template file for setting the GEMINI_API_KEY
├── .gitignore # Tells git to ignore databases, environments, and node packages
├── database.js # Initializes the SQLite database and executes DB table schemas
Expand Down
115 changes: 115 additions & 0 deletions css/animations.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* Motion system — keyframes & stagger utilities */

@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeInScale {
from { opacity: 0; transform: scale(0.96) translateY(6px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes popIn {
0% { transform: scale(0.92); opacity: 0; }
70% { transform: scale(1.02); }
100% { transform: scale(1); opacity: 1; }
}

@keyframes slideInRight {
from { opacity: 0; transform: translateX(-12px); }
to { opacity: 1; transform: translateX(0); }
}

@keyframes slideInUp {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}

@keyframes modalBackdropIn {
from { opacity: 0; backdrop-filter: blur(0); }
to { opacity: 1; backdrop-filter: blur(12px); }
}

@keyframes modalCardIn {
from {
opacity: 0;
transform: translateY(20px) scale(0.94);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}

@keyframes spin {
to { transform: rotate(360deg); }
}

@keyframes pulseGlow {
0%, 100% { box-shadow: var(--shadow-md); }
50% { box-shadow: var(--shadow-glow); }
}

@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}

@keyframes calDayPop {
0% { transform: scale(0.85); opacity: 0.6; }
100% { transform: scale(1); opacity: 1; }
}

@keyframes checkDraw {
from { transform: scale(0.6); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}

@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}

@keyframes floatOrb {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(2%, -1%) scale(1.02); }
66% { transform: translate(-1%, 2%) scale(0.98); }
}

@keyframes navIndicator {
from { opacity: 0; transform: scaleY(0.5); }
to { opacity: 1; transform: scaleY(1); }
}

@keyframes toastIn {
from { opacity: 0; transform: translateX(-50%) translateY(24px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}

@keyframes confFillGrow {
from { width: 0; }
}

/* Stagger children */
.stagger-children > * {
animation: slideInUp var(--duration-normal) var(--ease-emphasized) backwards;
}

.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
.stagger-children > *:nth-child(2) { animation-delay: 40ms; }
.stagger-children > *:nth-child(3) { animation-delay: 80ms; }
.stagger-children > *:nth-child(4) { animation-delay: 120ms; }
.stagger-children > *:nth-child(5) { animation-delay: 160ms; }
.stagger-children > *:nth-child(6) { animation-delay: 200ms; }
.stagger-children > *:nth-child(7) { animation-delay: 240ms; }
.stagger-children > *:nth-child(8) { animation-delay: 280ms; }
.stagger-children > *:nth-child(n+9) { animation-delay: 320ms; }

.animate-fade-in {
animation: fadeIn var(--duration-slow) var(--ease-emphasized);
}

.animate-pop-in {
animation: popIn var(--duration-normal) var(--ease-spring);
}
107 changes: 107 additions & 0 deletions css/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* Base — reset, typography, ambient background */

*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html {
font-size: clamp(0.9375rem, 0.25vw + 0.9rem, 1rem);
scroll-behavior: smooth;
}

body {
font-family: var(--font-sans);
font-size: var(--font-size-base);
line-height: var(--line-height-body);
color: var(--color-text-primary);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-5) var(--space-3);
overflow-x: hidden;
background-color: var(--color-background-tertiary);
background-image: var(--gradient-mesh-1), var(--gradient-mesh-2), var(--gradient-mesh-3);
background-attachment: fixed;
position: relative;
}

body::before {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
z-index: -1;
opacity: 0.6;
animation: floatOrb 18s ease-in-out infinite;
background: var(--gradient-mesh-1);
}

h1, h2, h3, .site-title, .modal-card__title, .topbar-title {
font-family: var(--font-display);
line-height: var(--line-height-tight);
color: var(--color-text-primary);
letter-spacing: -0.02em;
}

.site-title {
background: var(--gradient-brand);
background-size: 200% 200%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradientShift 8s ease infinite;
}

a {
color: var(--color-brand-600);
transition: color var(--duration-fast) var(--ease-standard);
}

a:hover,
a:focus-visible {
color: var(--color-violet);
}

:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}

button:focus:not(:focus-visible),
a:focus:not(:focus-visible) {
box-shadow: none;
}

.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

.hidden {
display: none !important;
}

@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}

.site-title {
-webkit-text-fill-color: var(--color-text-primary);
background: none;
}
}
Loading