diff --git a/opal-web/src/app.css b/opal-web/src/app.css new file mode 100644 index 0000000..20f40c8 --- /dev/null +++ b/opal-web/src/app.css @@ -0,0 +1,186 @@ +/* Global Styles - Mobile-First */ + +:root { + /* Colors */ + --color-primary: #4f46e5; + --color-primary-dark: #4338ca; + --color-secondary: #6b7280; + --color-success: #10b981; + --color-danger: #ef4444; + --color-warning: #f59e0b; + + /* Backgrounds */ + --bg-primary: #ffffff; + --bg-secondary: #f9fafb; + --bg-tertiary: #f3f4f6; + + /* Text */ + --text-primary: #111827; + --text-secondary: #6b7280; + --text-tertiary: #9ca3af; + + /* Borders */ + --border-color: #e5e7eb; + --border-radius: 0.5rem; + + /* Spacing */ + --spacing-xs: 0.25rem; + --spacing-sm: 0.5rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + --spacing-xl: 2rem; + + /* Typography */ + --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + --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; + + /* Layout */ + --nav-height: 60px; + --content-max-width: 768px; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1); +} + +/* Reset & Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-family: var(--font-sans); + font-size: 16px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + background-color: var(--bg-secondary); + color: var(--text-primary); + min-height: 100vh; + overflow-x: hidden; +} + +/* Typography */ +h1 { + font-size: var(--font-size-2xl); + font-weight: 700; + line-height: 1.2; + margin-bottom: var(--spacing-md); +} + +h2 { + font-size: var(--font-size-xl); + font-weight: 600; + line-height: 1.3; + margin-bottom: var(--spacing-sm); +} + +h3 { + font-size: var(--font-size-lg); + font-weight: 600; + line-height: 1.4; + margin-bottom: var(--spacing-sm); +} + +p { + margin-bottom: var(--spacing-md); +} + +a { + color: var(--color-primary); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Layout Helpers */ +.container { + max-width: var(--content-max-width); + margin: 0 auto; + padding: 0 var(--spacing-md); +} + +.page { + min-height: calc(100vh - var(--nav-height)); + padding-bottom: calc(var(--nav-height) + var(--spacing-md)); +} + +/* Utility Classes */ +.text-center { + text-align: center; +} + +.text-sm { + font-size: var(--font-size-sm); +} + +.text-secondary { + color: var(--text-secondary); +} + +.mt-sm { margin-top: var(--spacing-sm); } +.mt-md { margin-top: var(--spacing-md); } +.mt-lg { margin-top: var(--spacing-lg); } +.mb-sm { margin-bottom: var(--spacing-sm); } +.mb-md { margin-bottom: var(--spacing-md); } +.mb-lg { margin-bottom: var(--spacing-lg); } + +.hidden { + display: none; +} + +/* Loading State */ +.loading { + display: inline-block; + width: 1rem; + height: 1rem; + border: 2px solid var(--color-primary); + border-radius: 50%; + border-top-color: transparent; + animation: spin 0.6s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Touch Targets - Mobile First */ +button, +a, +input, +select, +textarea { + min-height: 44px; + min-width: 44px; +} + +/* Focus Styles */ +:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +/* Smooth Scrolling */ +html { + scroll-behavior: smooth; +} + +/* Safe Area Padding (for mobile notches) */ +@supports (padding: env(safe-area-inset-bottom)) { + .page { + padding-bottom: calc(var(--nav-height) + var(--spacing-md) + env(safe-area-inset-bottom)); + } +} diff --git a/opal-web/src/lib/components/BottomNav.svelte b/opal-web/src/lib/components/BottomNav.svelte new file mode 100644 index 0000000..c655a09 --- /dev/null +++ b/opal-web/src/lib/components/BottomNav.svelte @@ -0,0 +1,91 @@ + + + + + diff --git a/opal-web/src/lib/components/ui/Button.svelte b/opal-web/src/lib/components/ui/Button.svelte new file mode 100644 index 0000000..1867e07 --- /dev/null +++ b/opal-web/src/lib/components/ui/Button.svelte @@ -0,0 +1,115 @@ + + + + + diff --git a/opal-web/src/lib/components/ui/Checkbox.svelte b/opal-web/src/lib/components/ui/Checkbox.svelte new file mode 100644 index 0000000..ae1d926 --- /dev/null +++ b/opal-web/src/lib/components/ui/Checkbox.svelte @@ -0,0 +1,86 @@ + + + + + diff --git a/opal-web/src/lib/components/ui/Input.svelte b/opal-web/src/lib/components/ui/Input.svelte new file mode 100644 index 0000000..c56298e --- /dev/null +++ b/opal-web/src/lib/components/ui/Input.svelte @@ -0,0 +1,87 @@ + + +
Mobile-first task management
+ + {#if error} + + {/if} + + + ++ Or use API Key authentication +
+Projects view coming soon...
+
+ For testing, you can authenticate with an API key. Generate a key using:
+ opal server keygen --name "Web"
+
+ Or login with OAuth +
+Tags view coming soon...
+