Design sign-in flow

This commit is contained in:
Jason Zimdars
2024-07-24 17:42:43 -05:00
parent e139b5a72b
commit ac38edd49e
13 changed files with 155 additions and 34 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m23.6 11.289-9.793-9.7a2.607 2.607 0 0 0-3.679.075 2.64 2.64 0 0 0-.068 3.689l3.871 3.714a.25.25 0 0 1-.173.43H2.135A2.28 2.28 0 0 0 .1 12c0 .815.448 2.51 2 2.51h11.679a.25.25 0 0 1 .177.427l-3.731 3.733a2.66 2.66 0 0 0 3.758 3.754l9.625-9.72a1 1 0 0 0-.008-1.415"/></svg>

After

Width:  |  Height:  |  Size: 341 B

+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m23.888 5.832a.182.182 0 0 0 -.2.039l-9.747 9.745a2.75 2.75 0 0 1 -3.888 0l-9.743-9.745a.18.18 0 0 0 -.2-.039.182.182 0 0 0 -.11.168v12a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-12a.181.181 0 0 0 -.112-.168z"/><path d="m11.115 14.556a1.252 1.252 0 0 0 1.768 0l9.686-9.686a.5.5 0 0 0 .121-.511c-.11-.329-.416-.359-.69-.359h-20c-.275 0-.583.03-.691.359a.5.5 0 0 0 .121.511z"/></svg>

After

Width:  |  Height:  |  Size: 439 B

+1
View File
@@ -0,0 +1 @@
<svg enable-background="new 0 0 24 24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12.2" cy="12.1" fill="#4183bf" r="9.3"/><path d="m17.9 17.4c-.3-.8-1-1.4-1.9-1.4h-1v-3c0-.6-.4-1-1-1h-6v-2h2c.6 0 1-.4 1-1v-2h2c1.1 0 2-.9 2-2v-.4c2.9 1.2 5 4.1 5 7.4 0 2.1-.8 4-2.1 5.4m-6.9 2.5c-3.9-.5-7-3.9-7-7.9 0-.6.1-1.2.2-1.8l4.8 4.8v1c0 1.1.9 2 2 2m1-16c-5.5 0-10 4.5-10 10s4.5 10 10 10 10-4.5 10-10-4.5-10-10-10z" fill="#40c149"/></svg>

After

Width:  |  Height:  |  Size: 450 B

+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m17.508 18.5a1 1 0 0 0 -1 1v.5a.5.5 0 0 1 -.5.5h-5.5a.5.5 0 0 1 -.5-.5v-16a.5.5 0 0 1 .5-.5h5.5a.5.5 0 0 1 .5.5v1a1 1 0 0 0 2 0v-2.5a1 1 0 0 0 -1-1h-7.5v-1a.5.5 0 0 0 -.608-.488l-9 2a.5.5 0 0 0 -.392.488v19a.5.5 0 0 0 .392.489l9 2a.506.506 0 0 0 .421-.1.5.5 0 0 0 .187-.39v-1h7.5a1 1 0 0 0 1-1v-2a1 1 0 0 0 -1-.999zm-10-6a1.5 1.5 0 1 1 -1.5-1.5 1.5 1.5 0 0 1 1.5 1.5z"/><path d="m23.546 11.668-4.875-3.25a1 1 0 0 0 -1.554.832v1.75h-4.125a1.5 1.5 0 0 0 0 3h4.125v1.75a1 1 0 0 0 1.554.832l4.875-3.25a1 1 0 0 0 0-1.664z"/></svg>

After

Width:  |  Height:  |  Size: 594 B

+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m21.5 2.5h-19-.00000011c-1.38071.00000006-2.5 1.11929-2.5 2.5v14 .00000038c.00000021 1.38071 1.11929 2.5 2.5 2.5h19 .00000011c1.38071-.00000027 2.5-1.11929 2.5-2.5v-14c0-1.38071-1.11929-2.5-2.5-2.5zm-.5 12.25h-4-.00000003c-.414214-.00000002-.75-.335786-.75-.75.00000002-.414214.335786-.75.75-.75h4-.00000003c.414214-.00000002.75.335786.75.75.00000002.414214-.335786.75-.75.75zm-6.469-1.28c.292711.292294.293049.766535.00075431 1.05925-.00025126.00025162-.0005027.00050305-.00075431.00075431l-.00000003.00000002c-.29683.283557-.76417.283557-1.061-.00000005l-1.293-1.293-.00000001-.00000001c-.0975674-.0972627-.255433-.0972627-.353.00000002l-1.293 1.293.00000001-.00000001c-.294501.28931-.766499.28931-1.061.00000002l.00000002.00000002c-.292711-.292294-.293049-.766535-.00075434-1.05925.00025126-.00025162.0005027-.00050305.00075431-.00075431l1.293-1.293.00000001-.00000001c.0977544-.0975076.0979545-.255799.00044687-.353553-.00014878-.00014915-.00029774-.00029812-.00044689-.00044689l-1.293-1.293-.00000005-.00000006c-.28762-.298073-.279147-.772871.0189256-1.06049.290776-.280579.751564-.280362 1.04207.00049142l1.293 1.293c.0975674.0972627.255433.0972627.353-.00000001l1.293-1.293.00000005-.00000005c.297801-.287901.772607-.279876 1.06051.0179256.280853.290511.28107.751299.00049121 1.04207l-1.293 1.293-.00000003.00000003c-.0977544.0975076-.0979544.255799-.00044684.353553.00014878.00014915.00029774.00029812.00044689.00044689zm-7 0c.292711.292294.293049.766535.00075431 1.05925-.00025126.00025162-.0005027.00050305-.00075431.00075431l.00000003-.00000003c-.294812.287593-.765187.287593-1.06.00000005l-1.294-1.293c-.0981335-.0968348-.255866-.0968348-.354 0l-1.293 1.293.00000001-.00000001c-.296698.282921-.763302.282921-1.06.00000002l.00000002.00000002c-.292711-.292294-.293049-.766535-.00075434-1.05925.00025126-.00025162.0005027-.00050305.00075431-.00075431l1.293-1.293c.0968348-.0981335.0968348-.255866-.00000001-.354l-1.293-1.293-.00000002-.00000002c-.282379-.303043-.265627-.777621.0374166-1.06.28803-.268389.734554-.268389 1.02258.00000004l1.293 1.293c.0981335.0968348.255866.0968348.354.00000001l1.293-1.293.00000002-.00000002c.303043-.282379.777621-.265627 1.06.0374166.268389.28803.268389.734554-.00000003 1.02258l-1.293 1.293c-.0968348.0981335-.0968348.255866 0 .354z"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+12
View File
@@ -0,0 +1,12 @@
/* Animation classes */
.shake {
animation: shake 400ms both;
}
/* Keyframes */
@keyframes shake {
0% { transform: translateX(-2rem); }
25% { transform: translateX(2rem); }
50% { transform: translateX(-1rem); }
75% { transform: translateX(1rem); }
}
+1 -21
View File
@@ -32,17 +32,9 @@ body {
:where(#footer) {
max-inline-size: 100vw;
view-transition-name: footer;
> nav {
padding-inline: var(--inline-space);
view-transition-name: footer-nav;
}
&:has(.new-book-btn) {
inset-block-end: 0;
pointer-events: none;
position: sticky;
}
}
@@ -67,19 +59,7 @@ body {
}
}
:where(#toolbar) {
display: flex;
inset: 0 0 auto;
justify-content: center;
min-width: 0;
overflow: hidden;
padding-inline: var(--inline-space);
position: sticky;
view-transition-name: toolbar;
z-index: 1;
}
:is(#header, #footer, #toolbar) {
:is(#header, #footer) {
@media print {
display: none;
}
+33
View File
@@ -0,0 +1,33 @@
/* Language translations */
.lanuage-list-menu {
--max-width: 40ch;
background-color: var(--color-subtle);
border: 1px solid var(--color-subtle-dark);
border-radius: 0.5em;
inset: auto;
inline-size: max-content;
margin-inline: var(--inline-space);
max-inline-size: 40ch;
overflow: clip;
position: absolute;
z-index: 1;
@media (max-width: 70ch) {
max-inline-size: calc(var(--max-width) - var(--inline-space));
}
.popover-orientation-top & {
inset-block-end: 100%;
}
}
.language-list {
display: grid;
gap: var(--block-space-half) var(--inline-space);
grid-template-rows: min-content;
grid-template-columns: min-content 1fr;
justify-content: start;
margin: 0;
text-align: start;
}
+27
View File
@@ -0,0 +1,27 @@
module TranslationsHelper
TRANSLATIONS = {
email_address: { "🇺🇸": "Enter your email address", "🇪🇸": "Introduce tu correo electrónico", "🇫🇷": "Entrez votre adresse courriel", "🇮🇳": "अपना ईमेल पता दर्ज करें", "🇩🇪": "Geben Sie Ihre E-Mail-Adresse ein", "🇧🇷": "Insira seu endereço de email" },
password: { "🇺🇸": "Enter your password", "🇪🇸": "Introduce tu contraseña", "🇫🇷": "Saisissez votre mot de passe", "🇮🇳": "अपना पासवर्ड दर्ज करें", "🇩🇪": "Geben Sie Ihr Passwort ein", "🇧🇷": "Insira sua senha" }
}
def translations_for(translation_key)
tag.dl(class: "language-list") do
TRANSLATIONS[translation_key].map do |language, translation|
concat tag.dt(language)
concat tag.dd(translation, class: "margin-none")
end
end
end
def translation_button(translation_key)
tag.div(class: "position-relative", data: { controller: "popover", action: "keydown.esc->popover#close click@document->popover#closeOnClickOutside", popover_orientation_top_class: "popover-orientation-top" }) do
tag.button(type: "button", class: "btn", tabindex: -1, data: { action: "popover#toggle" }) do
concat image_tag("globe.svg", size: 20, role: "presentation", class: "color-icon")
concat tag.span("Translate", class: "for-screen-reader")
end +
tag.dialog(class: "lanuage-list-menu popover shadow", data: { popover_target: "menu" }) do
translations_for(translation_key)
end
end
end
end
@@ -0,0 +1,43 @@
import { Controller } from "@hotwired/stimulus"
const BOTTOM_THRESHOLD = 0
export default class extends Controller {
static targets = [ "menu" ]
static classes = [ "orientationTop" ]
close() {
this.menuTarget.close()
this.#orient()
}
open() {
this.menuTarget.show()
this.#orient()
}
toggle() {
this.menuTarget.open ? this.close() : this.open()
}
closeOnClickOutside({ target }) {
if (!this.element.contains(target)) this.close()
}
#orient() {
this.element.classList.toggle(this.orientationTopClass, this.#distanceToBottom < BOTTOM_THRESHOLD)
this.menuTarget.style.setProperty("--max-width", this.#maxWidth + "px")
}
get #distanceToBottom() {
return window.innerHeight - this.#boundingClientRect.bottom
}
get #maxWidth() {
return window.innerWidth - this.#boundingClientRect.left
}
get #boundingClientRect() {
return this.menuTarget.getBoundingClientRect()
}
}
+30 -6
View File
@@ -1,9 +1,33 @@
<%= form_with model: User.new, url: session_path do |f| %>
<%= f.label :email_address %>
<%= f.email_field :email_address %>
<% content_for(:title) { "Sign in" } %>
<% turbo_page_requires_reload %>
<%= f.label :password %>
<%= f.password_field :password %>
<div class="panel shadow center margin-block-double <%= "shake" if flash[:alert] %>" style="--panel-size: 40ch;">
<%= image_tag "splat-1.svg", class: "product__logo center colorize--black", size: 130 %>
<h1 class="margin-none-block-start margin-block-end-double">Splat</h1>
<%= f.submit "Sign in" %>
<%= form_with model: User.new, url: session_path, class: "flex flex-column gap" do |form| %>
<div class="flex align-center gap">
<%= translation_button(:email_address) %>
<label class="flex align-center gap input input--actor txt-large">
<%= form.email_field :email_address, required: true, class: "input full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %>
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
</label>
</div>
<div class="flex align-center gap">
<%= translation_button(:password) %>
<label class="flex align-center gap input input--actor txt-large">
<%= form.password_field :password, required: true, class: "input full-width", autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72 %>
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
</label>
</div>
<button type="submit" id="log_in" class="btn btn--reversed center">
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
<span class="for-screen-reader">Sign in</span>
</button>
<% end %>
</div>
<% content_for(:footer) do %>
<div class="txt-align-center center margin-block-double txt-subtle">Splat&trade; version 0</div>
<% end %>
-3
View File
@@ -1,3 +0,0 @@
<h1>Hello <%= Current.user.name %></h1>
<%= button_to "Sign out", session_path, method: :delete %>
+4 -4
View File
@@ -1,9 +1,9 @@
<% content_for :header do %>
<nav>
<button class="btn flex-item-justify-start">
<%= image_tag "settings.svg", aria: { hidden: true }, size: 24 %>
<span class="for-screen-reader">Settings</span>
</button>
<%= button_to session_path, method: :delete, class: "btn", form_class: "flex-item-justify-start" do %>
<%= image_tag "logout.svg", aria: { hidden: true }, size: 24 %>
<span class="for-screen-reader">Sign out</span>
<% end %>
<h1 class="txt-xx-large margin-none flex align-center gap-half">
Splats