diff --git a/src/BlazorApp/Components/App.razor b/src/BlazorApp/Components/App.razor index cffc23b..c5fff8c 100644 --- a/src/BlazorApp/Components/App.razor +++ b/src/BlazorApp/Components/App.razor @@ -6,9 +6,7 @@ - - - + diff --git a/src/BlazorApp/wwwroot/css/animations.css b/src/BlazorApp/wwwroot/css/animations.css new file mode 100644 index 0000000..4101cf5 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/animations.css @@ -0,0 +1,110 @@ +/* Animations */ + +/* Fade In */ +.fade-in { + animation: fadeInAnimation ease 1s; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +@keyframes fadeInAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* Slide */ +@keyframes slideInRight { + from { + transform: translateX(100%); + } + to { + transform: translateX(0); + } +} + +@keyframes slideOutRight { + from { + transform: translateX(0); + } + to { + transform: translateX(100%); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in-up { + animation: fadeInUp var(--transition-slow) var(--transition-timing); +} + +/* Spinner */ +.spinner { + display: inline-block; + width: 24px; + height: 24px; + border: 3px solid var(--color-slate-700); + border-top-color: var(--color-slate-50); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Pulse */ +.pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +/* Transitions */ +.transition { + transition: all var(--transition-base) var(--transition-timing); +} + +.transition-colors { + transition: color var(--transition-base) var(--transition-timing), + background-color var(--transition-base) var(--transition-timing), + border-color var(--transition-base) var(--transition-timing); +} + +.transition-transform { + transition: transform var(--transition-base) var(--transition-timing); +} + +.transition-opacity { + transition: opacity var(--transition-base) var(--transition-timing); +} + +/* Hover */ +.hover-scale:hover { + transform: scale(1.05); +} + +.hover-lift:hover { + transform: translateY(-2px); +} diff --git a/src/BlazorApp/wwwroot/css/app.css b/src/BlazorApp/wwwroot/css/app.css new file mode 100644 index 0000000..6afa7b4 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/app.css @@ -0,0 +1,8 @@ +/* Main Stylesheet */ + +@import url('variables.css'); +@import url('reset.css'); +@import url('base.css'); +@import url('layout.css'); +@import url('components.css'); +@import url('animations.css'); diff --git a/src/BlazorApp/wwwroot/css/base.css b/src/BlazorApp/wwwroot/css/base.css new file mode 100644 index 0000000..e3a9b35 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/base.css @@ -0,0 +1,90 @@ +/* Base Styles */ + +body { + background-color: var(--color-black); + color: var(--color-slate-50); + font-family: var(--font-mono); + font-size: var(--font-size-base); + line-height: var(--line-height-normal); +} + +/* Typography */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-tight); +} + +h1 { + font-size: var(--font-size-3xl); +} + +h2 { + font-size: var(--font-size-2xl); +} + +h3 { + font-size: var(--font-size-xl); +} + +p { + margin-bottom: var(--space-4); +} + +/* Links */ +a { + color: var(--color-slate-50); + transition: color var(--transition-base) var(--transition-timing); +} + +a:hover { + color: var(--color-slate-300); +} + +a:focus-visible { + outline: 2px solid var(--color-slate-50); + outline-offset: 2px; +} + +/* Focus */ +*:focus-visible { + outline: 2px solid var(--color-slate-50); + outline-offset: 2px; +} + +button:focus-visible, +input:focus-visible, +textarea:focus-visible, +select:focus-visible { + outline: 2px solid var(--color-slate-50); + outline-offset: 2px; +} + +/* Selection */ +::selection { + background-color: var(--color-slate-50); + color: var(--color-black); +} + +/* Scrollbar */ +body::-webkit-scrollbar { + width: 10px; +} + +body::-webkit-scrollbar-track { + background: var(--color-white); +} + +body::-webkit-scrollbar-thumb { + background-color: var(--color-black); + border: 1px solid var(--color-white); +} + +body { + scrollbar-width: thin; + scrollbar-color: var(--color-black) var(--color-white); +} diff --git a/src/BlazorApp/wwwroot/css/components.css b/src/BlazorApp/wwwroot/css/components.css new file mode 100644 index 0000000..1a5377d --- /dev/null +++ b/src/BlazorApp/wwwroot/css/components.css @@ -0,0 +1,214 @@ +/* Components */ + +/* Buttons */ +.btn { + display: inline-block; + padding: var(--space-3) var(--space-6); + font-weight: var(--font-weight-medium); + text-align: center; + border-radius: var(--radius-md); + transition: all var(--transition-base) var(--transition-timing); + cursor: pointer; +} + +.btn:focus-visible { + outline: 2px solid var(--color-slate-50); + outline-offset: 2px; +} + +.btn-primary { + background-color: var(--color-slate-50); + color: var(--color-black); +} + +.btn-primary:hover { + background-color: var(--color-slate-200); +} + +.btn-secondary { + background-color: transparent; + color: var(--color-slate-50); + border: 1px solid var(--color-slate-50); +} + +.btn-secondary:hover { + background-color: var(--color-slate-50); + color: var(--color-black); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Inputs */ +.input { + width: 100%; + padding: var(--space-3); + background-color: var(--color-black); + border: 1px solid var(--color-slate-700); + border-radius: var(--radius-md); + color: var(--color-slate-50); + font-size: var(--font-size-base); +} + +.input:focus { + outline: none; + border-color: var(--color-slate-50); +} + +.input:invalid { + border-color: #ef4444; +} + +.input::placeholder { + color: var(--color-slate-500); +} + +/* Textareas */ +.textarea { + width: 100%; + padding: var(--space-3); + background-color: var(--color-black); + border: 1px solid var(--color-slate-700); + border-radius: var(--radius-md); + color: var(--color-slate-50); + font-size: var(--font-size-base); + resize: vertical; + min-height: 120px; +} + +.textarea:focus { + outline: none; + border-color: var(--color-slate-50); +} + +/* Labels */ +.label { + display: block; + margin-bottom: var(--space-2); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); +} + +/* Cards */ +.card { + background-color: var(--color-slate-900); + border-radius: var(--radius-lg); + padding: var(--space-6); + border: 1px solid var(--color-slate-800); +} + +/* Navigation */ +.nav { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-6) 0; +} + +.nav-link { + padding: var(--space-2) var(--space-4); + color: var(--color-slate-50); + font-size: var(--font-size-sm); + transition: color var(--transition-base) var(--transition-timing); +} + +.nav-link:hover { + color: var(--color-slate-300); +} + +.nav-link-active { + font-weight: var(--font-weight-semibold); +} + +/* Footer */ +.footer { + padding: var(--space-8) 0; + margin-top: auto; + border-top: 1px solid var(--color-slate-800); + text-align: center; + font-size: var(--font-size-sm); + color: var(--color-slate-400); +} + +/* Mobile Menu (CSS-only) */ +.mobile-menu-toggle { + display: none; +} + +.mobile-menu-button { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + cursor: pointer; + font-size: var(--font-size-2xl); +} + +.mobile-menu-overlay { + display: none; + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + z-index: var(--z-overlay); +} + +.mobile-menu { + display: none; + position: fixed; + top: 0; + right: 0; + bottom: 0; + width: 80%; + max-width: 300px; + background-color: var(--color-black); + border-left: 1px solid var(--color-slate-800); + padding: var(--space-6); + z-index: var(--z-modal); + transform: translateX(100%); + transition: transform var(--transition-slow) var(--transition-timing); +} + +.mobile-menu-toggle:checked ~ .mobile-menu-overlay { + display: block; +} + +.mobile-menu-toggle:checked ~ .mobile-menu { + display: block; + transform: translateX(0); +} + +.mobile-menu-close { + display: flex; + justify-content: flex-end; + margin-bottom: var(--space-6); + font-size: var(--font-size-2xl); + cursor: pointer; +} + +/* Links */ +.link { + color: var(--color-slate-50); + text-decoration: underline; + text-underline-offset: 2px; +} + +.link:hover { + color: var(--color-slate-300); +} + +/* Icon Buttons */ +.icon-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--space-2); + transition: opacity var(--transition-base) var(--transition-timing); +} + +.icon-button:hover { + opacity: 0.7; +} diff --git a/src/BlazorApp/wwwroot/css/layout.css b/src/BlazorApp/wwwroot/css/layout.css new file mode 100644 index 0000000..242bc25 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/layout.css @@ -0,0 +1,240 @@ +/* Layout */ + +/* Container */ +.container { + width: 100%; + max-width: var(--container-max-width); + margin-left: auto; + margin-right: auto; + padding-left: var(--space-6); + padding-right: var(--space-6); +} + +@media (min-width: 1024px) { + .container { + padding-left: var(--space-10); + padding-right: var(--space-10); + } +} + +/* Flexbox */ +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.flex-row { + flex-direction: row; +} + +.flex-1 { + flex: 1 1 0%; +} + +.items-start { + align-items: flex-start; +} + +.items-center { + align-items: center; +} + +.items-end { + align-items: flex-end; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-center { + justify-content: center; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-between { + justify-content: space-between; +} + +.gap-1 { + gap: var(--space-1); +} + +.gap-2 { + gap: var(--space-2); +} + +.gap-3 { + gap: var(--space-3); +} + +.gap-4 { + gap: var(--space-4); +} + +.gap-6 { + gap: var(--space-6); +} + +.gap-8 { + gap: var(--space-8); +} + +/* Grid */ +.grid { + display: grid; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +@media (min-width: 640px) { + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .sm\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (min-width: 768px) { + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .md\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} + +@media (min-width: 1024px) { + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .lg\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } +} + +/* Spacing */ +.min-h-screen { + min-height: 100vh; +} + +.p-2 { padding: var(--space-2); } +.p-4 { padding: var(--space-4); } +.p-6 { padding: var(--space-6); } +.p-8 { padding: var(--space-8); } + +.px-2 { padding-left: var(--space-2); padding-right: var(--space-2); } +.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); } +.px-6 { padding-left: var(--space-6); padding-right: var(--space-6); } +.px-8 { padding-left: var(--space-8); padding-right: var(--space-8); } + +.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); } +.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); } +.py-6 { padding-top: var(--space-6); padding-bottom: var(--space-6); } +.py-8 { padding-top: var(--space-8); padding-bottom: var(--space-8); } + +.m-2 { margin: var(--space-2); } +.m-4 { margin: var(--space-4); } +.m-6 { margin: var(--space-6); } +.m-8 { margin: var(--space-8); } + +.mx-auto { margin-left: auto; margin-right: auto; } + +.mt-2 { margin-top: var(--space-2); } +.mt-4 { margin-top: var(--space-4); } +.mt-6 { margin-top: var(--space-6); } +.mt-8 { margin-top: var(--space-8); } + +.mb-2 { margin-bottom: var(--space-2); } +.mb-4 { margin-bottom: var(--space-4); } +.mb-6 { margin-bottom: var(--space-6); } +.mb-8 { margin-bottom: var(--space-8); } + +/* Display */ +.hidden { + display: none; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +/* Responsive */ +@media (min-width: 1024px) { + .lg\:flex { + display: flex; + } + .lg\:hidden { + display: none; + } + .lg\:block { + display: block; + } +} + +/* Widths */ +.w-full { + width: 100%; +} + +.max-w-sm { + max-width: 24rem; +} + +.max-w-md { + max-width: 28rem; +} + +.max-w-lg { + max-width: 32rem; +} + +.max-w-xl { + max-width: 36rem; +} + +.max-w-2xl { + max-width: 42rem; +} + +.max-w-7xl { + max-width: 80rem; +} + +/* Text */ +.text-center { + text-align: center; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} diff --git a/src/BlazorApp/wwwroot/css/reset.css b/src/BlazorApp/wwwroot/css/reset.css new file mode 100644 index 0000000..5b8f715 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/reset.css @@ -0,0 +1,77 @@ +/* CSS Reset */ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; + padding: 0; +} + +html { + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + tab-size: 4; +} + +html:focus-within { + scroll-behavior: smooth; +} + +body { + min-height: 100vh; + line-height: 1.5; + text-rendering: optimizeSpeed; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} + +input, +button, +textarea, +select { + font: inherit; +} + +button { + background: none; + border: none; + cursor: pointer; +} + +ul, +ol { + list-style: none; +} + +a { + text-decoration: none; + color: inherit; +} + +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/src/BlazorApp/wwwroot/css/variables.css b/src/BlazorApp/wwwroot/css/variables.css new file mode 100644 index 0000000..b1cfc59 --- /dev/null +++ b/src/BlazorApp/wwwroot/css/variables.css @@ -0,0 +1,97 @@ +/* Design Tokens */ + +:root { + /* Colors */ + --color-black: #000000; + --color-white: #ffffff; + --color-slate-50: #f8fafc; + --color-slate-100: #f1f5f9; + --color-slate-200: #e2e8f0; + --color-slate-300: #cbd5e1; + --color-slate-400: #94a3b8; + --color-slate-500: #64748b; + --color-slate-600: #475569; + --color-slate-700: #334155; + --color-slate-800: #1e293b; + --color-slate-900: #0f172a; + --color-gray-700: #374151; + --color-gray-800: #1f2937; + + /* Typography */ + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + --font-size-3xl: 1.875rem; + --font-size-4xl: 2.25rem; + --font-size-5xl: 3rem; + --font-size-6xl: 3.75rem; + --line-height-tight: 1.25; + --line-height-normal: 1.5; + --line-height-relaxed: 1.75; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* Spacing */ + --space-0: 0; + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + --space-12: 3rem; + --space-16: 4rem; + --space-20: 5rem; + --space-24: 6rem; + + /* Breakpoints */ + --breakpoint-sm: 640px; + --breakpoint-md: 768px; + --breakpoint-lg: 1024px; + --breakpoint-xl: 1280px; + --breakpoint-2xl: 1536px; + + /* Container */ + --container-max-width: 80rem; + + /* Border Radius */ + --radius-sm: 0.125rem; + --radius-base: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + --radius-2xl: 1rem; + --radius-full: 9999px; + + /* Z-index */ + --z-base: 0; + --z-dropdown: 10; + --z-sticky: 20; + --z-fixed: 30; + --z-overlay: 40; + --z-modal: 50; + --z-popover: 60; + --z-tooltip: 70; + + /* Transitions */ + --transition-fast: 150ms; + --transition-base: 200ms; + --transition-slow: 300ms; + --transition-slower: 500ms; + --transition-timing: cubic-bezier(0.4, 0, 0.2, 1); + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); +}