diff --git a/app/assets/stylesheets/application.postcss.css b/app/assets/stylesheets/application.postcss.css index 0556688..b82dc97 100644 --- a/app/assets/stylesheets/application.postcss.css +++ b/app/assets/stylesheets/application.postcss.css @@ -9,6 +9,7 @@ @import "components/form.css"; @import "components/navigation.css"; +@import "components/session.css"; @import "components/sidebar.css"; @import "components/topics.css"; @import "components/avatars.css"; @@ -17,4 +18,5 @@ @import "components/notes.css"; @import "components/activities.css"; @import "components/profile.css"; +@import "components/settings.css"; @import "components/stats.css"; diff --git a/app/assets/stylesheets/components/navigation.css b/app/assets/stylesheets/components/navigation.css index 1ebedf3..67c243d 100644 --- a/app/assets/stylesheets/components/navigation.css +++ b/app/assets/stylesheets/components/navigation.css @@ -72,7 +72,7 @@ text-decoration: none; font-weight: var(--font-weight-medium); padding: var(--spacing-2) var(--spacing-4); - border-radius: var(--border-radius-md); + border-radius: 999px; transition: all var(--transition-fast); border: var(--border-width) solid transparent; display: inline-flex; @@ -113,6 +113,7 @@ color: var(--color-text-secondary); cursor: pointer; box-shadow: var(--shadow-sm); + border-radius: 999px; &:hover { background: var(--color-bg-hover); diff --git a/app/assets/stylesheets/components/session.css b/app/assets/stylesheets/components/session.css new file mode 100644 index 0000000..cfdde97 --- /dev/null +++ b/app/assets/stylesheets/components/session.css @@ -0,0 +1,171 @@ +.session-page { + min-height: calc(100vh - var(--nav-height)); + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-10) var(--spacing-4) var(--spacing-12); + background: var(--color-bg-container); +} + +.session-card { + width: 100%; + max-width: 420px; + padding: var(--spacing-12) var(--spacing-4); + border: none; + background: transparent; + box-shadow: none; +} + +.session-title { + margin: 0 0 var(--spacing-8); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); +} + +.session-text { + margin: 0 0 var(--spacing-6); + color: var(--color-text-muted); + font-size: var(--font-size-sm); + line-height: var(--line-height-relaxed); +} + +.session-oauth { + margin-bottom: var(--spacing-6); +} + +.session-oauth-button { + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: var(--spacing-3); + width: 100%; + padding: var(--spacing-3) var(--spacing-6); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-card); + color: var(--color-text-primary); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + text-decoration: none; + transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast); +} + +.session-oauth-button:hover { + border-color: var(--color-primary-300); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.session-oauth-icon { + position: absolute; + left: var(--spacing-5); + display: inline-flex; + align-items: center; +} + +.session-oauth-svg { + display: block; +} + +.session-divider { + display: flex; + align-items: center; + gap: var(--spacing-4); + margin: var(--spacing-6) 0 var(--spacing-4); + color: var(--color-text-muted); + font-size: var(--font-size-xs); + letter-spacing: 0.08em; +} + +.session-divider::before, +.session-divider::after { + content: ""; + flex: 1; + height: 1px; + background: var(--color-border); +} + +.session-form { + margin-top: var(--spacing-2); +} + +.session-field { + margin-bottom: var(--spacing-4); +} + +.session-input { + width: 100%; + padding: var(--spacing-4) var(--spacing-6); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-input); + font-size: var(--font-size-sm); + margin: 0; +} + +.session-input:focus { + outline: none; + border-color: var(--color-border-focus); + box-shadow: var(--shadow-focus); +} + +.session-actions { + margin-top: var(--spacing-6); +} + +.session-submit { + width: 100%; + padding: var(--spacing-4); + border: none; + border-radius: 999px; + background: #d9480f; + color: #ffffff; + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + cursor: pointer; + transition: background-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast); +} + +.session-submit:hover { + background: #bf3f0c; + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.session-links { + margin-top: var(--spacing-6); + display: flex; + flex-direction: column; + align-items: center; + gap: var(--spacing-2); + font-size: var(--font-size-sm); +} + +.session-hint { + display: block; + margin-top: var(--spacing-2); + color: var(--color-text-muted); + font-size: var(--font-size-xs); +} + +.session-link { + color: var(--color-text-link); + text-decoration: none; +} + +.session-link:hover { + color: var(--color-text-link-hover); + text-decoration: underline; +} + +@media (max-width: 480px) { + .session-card { + padding: var(--spacing-10) var(--spacing-2); + } + + .session-title { + margin-bottom: var(--spacing-6); + } +} diff --git a/app/assets/stylesheets/components/settings.css b/app/assets/stylesheets/components/settings.css new file mode 100644 index 0000000..5876057 --- /dev/null +++ b/app/assets/stylesheets/components/settings.css @@ -0,0 +1,133 @@ +.settings-page { + padding: var(--spacing-10) var(--spacing-6) var(--spacing-12); +} + +.settings-page h1 { + font-size: var(--font-size-2xl); + color: var(--color-text-primary); + margin-bottom: var(--spacing-8); +} + +.settings-section { + padding: var(--spacing-6) 0; + border-bottom: var(--border-width) solid var(--color-border); +} + +.settings-section:last-child { + border-bottom: none; +} + +.settings-section h2 { + margin-bottom: var(--spacing-4); + color: var(--color-text-primary); +} + +.settings-warning { + margin-bottom: var(--spacing-4); + color: var(--color-warning-text); + background: var(--color-warning-bg); + padding: var(--spacing-3) var(--spacing-4); + border-radius: 999px; + display: inline-flex; + align-items: center; +} + +.settings-page .form-group { + margin-bottom: var(--spacing-4); +} + +.settings-page .form-group label { + display: block; + margin-bottom: var(--spacing-2); + color: var(--color-text-secondary); + font-weight: var(--font-weight-medium); +} + +.settings-page input[type="text"], +.settings-page input[type="email"], +.settings-page input[type="password"] { + width: 100%; + padding: var(--spacing-4) var(--spacing-6); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-input); + font-size: var(--font-size-sm); + margin: 0; +} + +.settings-page input[type="text"]:focus, +.settings-page input[type="email"]:focus, +.settings-page input[type="password"]:focus { + outline: none; + border-color: var(--color-border-focus); + box-shadow: var(--shadow-focus); +} + +.settings-page .button-primary, +.settings-page .button-secondary, +.settings-page .button-danger, +.settings-page input[type="submit"] { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--spacing-3) var(--spacing-6); + border-radius: 999px; + font-weight: var(--font-weight-semibold); + text-decoration: none; + border: var(--border-width) solid transparent; + cursor: pointer; + transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast), background-color var(--transition-fast); +} + +.settings-page .button-primary, +.settings-page input[type="submit"].button-primary, +.settings-page input[type="submit"]:not(.button-secondary):not(.button-danger) { + background: var(--color-bg-button); + color: var(--color-text-button); + border-color: var(--color-border); +} + +.settings-page .button-primary:hover, +.settings-page input[type="submit"].button-primary:hover, +.settings-page input[type="submit"]:not(.button-secondary):not(.button-danger):hover { + background: #d9480f; + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.settings-page .button-secondary { + background: var(--color-bg-card); + color: var(--color-text-primary); + border-color: var(--color-border); +} + +.settings-page .button-secondary:hover { + border-color: var(--color-primary-300); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.settings-page .button-danger { + background: var(--color-bg-button); + color: var(--color-text-button); + border-color: var(--color-border); +} + +.settings-page .button-danger:hover { + background: #d9480f; + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.settings-google-button { + gap: var(--spacing-3); +} + +.settings-google-icon { + display: inline-flex; + align-items: center; +} + +.settings-google-svg { + display: block; +} diff --git a/app/assets/stylesheets/components/sidebar.css b/app/assets/stylesheets/components/sidebar.css index c88f8f3..7ba842c 100644 --- a/app/assets/stylesheets/components/sidebar.css +++ b/app/assets/stylesheets/components/sidebar.css @@ -8,6 +8,40 @@ margin-bottom: var(--spacing-8); } +.sidebar .search-input { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-input); + font-size: var(--font-size-sm); + margin: 0 0 var(--spacing-3); +} + +.sidebar .search-input:focus { + outline: none; + border-color: var(--color-border-focus); + box-shadow: var(--shadow-focus); +} + +.sidebar .search-button { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-card); + color: var(--color-text-primary); + font-weight: var(--font-weight-medium); + cursor: pointer; + transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast); +} + +.sidebar .search-button:hover { + border-color: var(--color-primary-300); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + .sidebar-section summary.sidebar-heading { list-style: none; display: block; diff --git a/app/assets/stylesheets/components/topics.css b/app/assets/stylesheets/components/topics.css index 9b2eb26..2e01a55 100644 --- a/app/assets/stylesheets/components/topics.css +++ b/app/assets/stylesheets/components/topics.css @@ -465,13 +465,20 @@ } .mark-aware-button { - padding: var(--spacing-2) var(--spacing-4); + padding: var(--spacing-3) var(--spacing-4); border: var(--border-width) solid var(--color-border); background: var(--color-bg-card); - border-radius: var(--border-radius-sm); + border-radius: 999px; cursor: pointer; font-weight: var(--font-weight-medium); color: var(--color-text-primary); + transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast); + + &:hover { + border-color: var(--color-primary-300); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); + } &.secondary { background: var(--color-bg-hover); diff --git a/app/views/passwords/edit.html.slim b/app/views/passwords/edit.html.slim index dc5248f..28a79a4 100644 --- a/app/views/passwords/edit.html.slim +++ b/app/views/passwords/edit.html.slim @@ -1,12 +1,17 @@ -h1 Set a new password +.session-page + .session-card + h1.session-title Set a new password -= form_with url: password_path, method: :patch, data: { turbo: false } do - = hidden_field_tag :token, @raw - .field - = label_tag :password - = password_field_tag :password - .field - = label_tag :password_confirmation - = password_field_tag :password_confirmation - .actions - = submit_tag 'Update password' + = form_with url: password_path, method: :patch, data: { turbo: false }, class: 'session-form' do + = hidden_field_tag :token, @raw + .session-field + = label_tag :password, 'Password', class: 'visually-hidden' + = password_field_tag :password, nil, placeholder: 'Password', autocomplete: 'new-password', class: 'session-input' + .session-field + = label_tag :password_confirmation, 'Confirm password', class: 'visually-hidden' + = password_field_tag :password_confirmation, nil, placeholder: 'Confirm password', autocomplete: 'new-password', class: 'session-input' + .session-actions + = submit_tag 'Update password', class: 'session-submit' + + .session-links + = link_to 'Back to Log In', new_session_path, class: 'session-link' diff --git a/app/views/passwords/new.html.slim b/app/views/passwords/new.html.slim index 65a75f2..c4b8d3b 100644 --- a/app/views/passwords/new.html.slim +++ b/app/views/passwords/new.html.slim @@ -1,8 +1,13 @@ -h1 Reset password +.session-page + .session-card + h1.session-title Reset password -= form_with url: password_path, method: :post, data: { turbo: false } do - .field - = label_tag :email - = email_field_tag :email - .actions - = submit_tag 'Send reset link' + = form_with url: password_path, method: :post, data: { turbo: false }, class: 'session-form' do + .session-field + = label_tag :email, 'Email', class: 'visually-hidden' + = email_field_tag :email, nil, placeholder: 'Email', autocomplete: 'email', class: 'session-input' + .session-actions + = submit_tag 'Send reset link', class: 'session-submit' + + .session-links + = link_to 'Back to Log In', new_session_path, class: 'session-link' diff --git a/app/views/registrations/new.html.slim b/app/views/registrations/new.html.slim index f2e549c..3d75c8f 100644 --- a/app/views/registrations/new.html.slim +++ b/app/views/registrations/new.html.slim @@ -1,23 +1,40 @@ -h1 Register +.session-page + .session-card + h1.session-title Register + p.session-text Enter your email. If it's in the archive, we'll verify and attach it. Otherwise, we'll create a new alias. -p Enter your email. If it's in the archive, we'll verify and attach it. Otherwise, we'll create a new alias. + .session-oauth + = link_to '/auth/google_oauth2', class: 'session-oauth-button' do + span.session-oauth-icon aria-hidden="true" + svg.session-oauth-svg width="18" height="18" viewBox="0 0 48 48" + path fill="#FFC107" d="M43.6 20.5H42V20H24v8h11.3C33.6 32.6 29.2 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20c11 0 20-9 20-20 0-1.3-.1-2.2-.4-3.5z" + path fill="#FF3D00" d="M6.3 14.7l6.6 4.8C14.7 16.2 19 14 24 14c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 16.2 4 9.5 8.3 6.3 14.7z" + path fill="#4CAF50" d="M24 44c5.1 0 9.9-1.9 13.5-5.1l-6.2-5.1C29.3 35.6 26.8 36.5 24 36.5c-5.1 0-9.4-3.4-11-8.1l-6.5 5C9.7 39.7 16.4 44 24 44z" + path fill="#1976D2" d="M43.6 20.5H42V20H24v8h11.3c-1.1 3.1-3.3 5.7-6.1 7.4l6.2 5.1C39.1 36.9 44 31.1 44 24c0-1.3-.1-2.2-.4-3.5z" + span.session-oauth-label Continue with Google -= form_with url: registration_path, method: :post, data: { turbo: false } do |f| - .field - = label_tag :email - = email_field_tag :email, nil, required: true, placeholder: "you@example.com" - .field - = label_tag :name, 'Alias name (for new email)' - = text_field_tag :name, nil, placeholder: "Your display name" - .field - = label_tag :username, 'Username' - = text_field_tag :username, nil, placeholder: "username", pattern: "[A-Za-z0-9_.-]+", required: true - small Allowed: letters, numbers, _, -, . - .field - = label_tag :password, 'Password' - = password_field_tag :password, nil, placeholder: "Password", required: true - .field - = label_tag :password_confirmation, 'Confirm password' - = password_field_tag :password_confirmation, nil, placeholder: "Confirm password", required: true - .actions - = submit_tag 'Send verification email' + .session-divider + span OR + + = form_with url: registration_path, method: :post, data: { turbo: false }, class: 'session-form' do |f| + .session-field + = label_tag :email, 'Email', class: 'visually-hidden' + = email_field_tag :email, nil, required: true, placeholder: "you@example.com", autocomplete: 'email', class: 'session-input' + .session-field + = label_tag :name, 'Alias name (for new email)', class: 'visually-hidden' + = text_field_tag :name, nil, placeholder: "Your display name", autocomplete: 'name', class: 'session-input' + .session-field + = label_tag :username, 'Username', class: 'visually-hidden' + = text_field_tag :username, nil, placeholder: "username", pattern: "[A-Za-z0-9_.-]+", required: true, autocomplete: 'username', class: 'session-input' + small.session-hint Allowed: letters, numbers, _, -, . + .session-field + = label_tag :password, 'Password', class: 'visually-hidden' + = password_field_tag :password, nil, placeholder: "Password", required: true, autocomplete: 'new-password', class: 'session-input' + .session-field + = label_tag :password_confirmation, 'Confirm password', class: 'visually-hidden' + = password_field_tag :password_confirmation, nil, placeholder: "Confirm password", required: true, autocomplete: 'new-password', class: 'session-input' + .session-actions + = submit_tag 'Send verification email', class: 'session-submit' + + .session-links + = link_to 'Already have an account? Log In', new_session_path, class: 'session-link' diff --git a/app/views/sessions/new.html.slim b/app/views/sessions/new.html.slim index 75b7a46..b9b3162 100644 --- a/app/views/sessions/new.html.slim +++ b/app/views/sessions/new.html.slim @@ -1,20 +1,30 @@ -h1 Sign in +.session-page + .session-card + h1.session-title Log In -= form_with url: session_path, method: :post, local: true, data: { turbo: false } do |f| - .field - = label_tag :email - = email_field_tag :email - .field - = label_tag :password - = password_field_tag :password - .actions - = submit_tag 'Sign in' + .session-oauth + = link_to '/auth/google_oauth2', class: 'session-oauth-button' do + span.session-oauth-icon aria-hidden="true" + svg.session-oauth-svg width="18" height="18" viewBox="0 0 48 48" + path fill="#FFC107" d="M43.6 20.5H42V20H24v8h11.3C33.6 32.6 29.2 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20c11 0 20-9 20-20 0-1.3-.1-2.2-.4-3.5z" + path fill="#FF3D00" d="M6.3 14.7l6.6 4.8C14.7 16.2 19 14 24 14c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 16.2 4 9.5 8.3 6.3 14.7z" + path fill="#4CAF50" d="M24 44c5.1 0 9.9-1.9 13.5-5.1l-6.2-5.1C29.3 35.6 26.8 36.5 24 36.5c-5.1 0-9.4-3.4-11-8.1l-6.5 5C9.7 39.7 16.4 44 24 44z" + path fill="#1976D2" d="M43.6 20.5H42V20H24v8h11.3c-1.1 3.1-3.3 5.7-6.1 7.4l6.2 5.1C39.1 36.9 44 31.1 44 24c0-1.3-.1-2.2-.4-3.5z" + span.session-oauth-label Continue with Google -.oauth - p or - = link_to 'Sign in with Google', '/auth/google_oauth2', class: 'button google' + .session-divider + span OR -.links - = link_to 'Register', new_registration_path - | ยท - = link_to 'Forgot password?', new_password_path + = form_with url: session_path, method: :post, local: true, data: { turbo: false }, class: 'session-form' do |f| + .session-field + = label_tag :email, 'Email', class: 'visually-hidden' + = email_field_tag :email, nil, placeholder: 'Email', autocomplete: 'username', class: 'session-input' + .session-field + = label_tag :password, 'Password', class: 'visually-hidden' + = password_field_tag :password, nil, placeholder: 'Password', autocomplete: 'current-password', class: 'session-input' + .session-actions + = submit_tag 'Log In', class: 'session-submit' + + .session-links + = link_to 'Forgot your password?', new_password_path, class: 'session-link' + = link_to 'Register', new_registration_path, class: 'session-link' diff --git a/app/views/settings/show.html.slim b/app/views/settings/show.html.slim index e000ff7..0ce8ecf 100644 --- a/app/views/settings/show.html.slim +++ b/app/views/settings/show.html.slim @@ -30,7 +30,14 @@ .settings-section h2 Connected accounts - = link_to "Connect Google account", "/auth/google_oauth2?link=1", class: "button-primary", data: { turbo: false } + = link_to "/auth/google_oauth2?link=1", class: "button-primary settings-google-button", data: { turbo: false } do + span.settings-google-icon aria-hidden="true" + svg.settings-google-svg width="18" height="18" viewBox="0 0 48 48" + path fill="#FFC107" d="M43.6 20.5H42V20H24v8h11.3C33.6 32.6 29.2 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20c11 0 20-9 20-20 0-1.3-.1-2.2-.4-3.5z" + path fill="#FF3D00" d="M6.3 14.7l6.6 4.8C14.7 16.2 19 14 24 14c3 0 5.8 1.1 7.9 2.9l5.7-5.7C34.5 6.1 29.5 4 24 4 16.2 4 9.5 8.3 6.3 14.7z" + path fill="#4CAF50" d="M24 44c5.1 0 9.9-1.9 13.5-5.1l-6.2-5.1C29.3 35.6 26.8 36.5 24 36.5c-5.1 0-9.4-3.4-11-8.1l-6.5 5C9.7 39.7 16.4 44 24 44z" + path fill="#1976D2" d="M43.6 20.5H42V20H24v8h11.3c-1.1 3.1-3.3 5.7-6.1 7.4l6.2 5.1C39.1 36.9 44 31.1 44 24c0-1.3-.1-2.2-.4-3.5z" + span Connect Google account - if @identities.any? table.email-table