update look and feel of login

This commit is contained in:
2025-10-03 20:26:35 -04:00
parent e63615eb46
commit 56ec490159
4 changed files with 364 additions and 84 deletions

View File

@@ -1,39 +1,67 @@
import { useState } from 'react';
import ILoginFormPops from './ILoginFormProps';
import { Input } from './Styled';
import type { ILoginFormProps } from './ILoginFormProps';
import { FlexForm, Input, Button, FormTitle, FormSubtitle, InputContainer, ErrorMessage } from './Styled';
export function LoginForm({ onSubmit }: ILoginFormProps) {
const [username, setUsername] = useState<string>('');
const [secret, setSecret] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string>('');
// The submit handler now correctly uses 'event'
const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
const handleFormSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(username, secret);
if (!username.trim() || !secret.trim()) {
setError('Please fill in all fields');
return;
}
setIsLoading(true);
setError('');
try {
await onSubmit(username, secret);
} catch (err) {
setError('Login failed. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
<form onSubmit={handleFormSubmit}>
{/*
- The 'placeholder' prop is now lowercase.
- The 'onChange' handler correctly destructures {target}.
*/}
<Input
type="text"
placeholder="Username"
autoComplete="username"
value={username}
onChange={({ target }) => setUsername(target.value)}
/>
<Input
type="password"
placeholder="Password"
autoComplete="current-password"
value={secret}
onChange={({ target }) => setSecret(target.value)}
/>
<button type="submit">Login</button>
</form>
<FlexForm onSubmit={handleFormSubmit}>
<FormTitle>Welcome Back</FormTitle>
<FormSubtitle>Sign in to your account to continue</FormSubtitle>
<InputContainer>
<Input
type="text"
placeholder="Enter your username"
autoComplete="username"
value={username}
onChange={({ target }) => setUsername(target.value)}
disabled={isLoading}
required
/>
</InputContainer>
<InputContainer>
<Input
type="password"
placeholder="Enter your password"
autoComplete="current-password"
value={secret}
onChange={({ target }) => setSecret(target.value)}
disabled={isLoading}
required
/>
</InputContainer>
{error && <ErrorMessage>{error}</ErrorMessage>}
<Button type="submit" disabled={isLoading}>
{isLoading ? 'Signing In...' : 'Sign In'}
</Button>
</FlexForm>
);
}

View File

@@ -1,15 +1,154 @@
import styled from 'styled-components';
import styled, { keyframes } from 'styled-components';
const fadeIn = keyframes`
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
const industrialGlow = keyframes`
0% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1);
}
50% {
box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.05);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1);
}
`;
export const FlexForm = styled.form`
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 3rem 3rem 3rem 3rem;
background: #2a2a2a;
border: 2px solid #111111;
border-radius: 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
animation: ${fadeIn} 0.6s ease-out;
width: 450px;
box-sizing: border-box;
&:focus {
outline: none;
}
`;
export const Input = styled.input`
width: 100%;
heght: 1.3rem;
backgroundColor: blue
`
padding: 1rem 1.25rem;
font-size: 0.9rem;
font-weight: 400;
font-family: 'Courier New', monospace;
background: #0a0a0a;
border: 1px solid #404040;
border-radius: 0;
color: #ffffff;
transition: all 0.2s ease;
text-transform: uppercase;
letter-spacing: 1px;
box-sizing: border-box;
&::placeholder {
color: #666666;
font-weight: 400;
text-transform: uppercase;
letter-spacing: 1px;
}
&:focus {
outline: none;
border-color: #ffffff;
background: #000000;
}
&:hover {
border-color: #666666;
}
`;
export const Button = styled.button`
width: fit-content;
margin: auto;
text-align: center;
background-color: #a7c42;
width: 100%;
padding: 1.1rem 1.5rem;
font-size: 0.95rem;
font-weight: 600;
font-family: 'Courier New', monospace;
background: #000000;
color: #ffffff;
border: 2px solid #ffffff;
border-radius: 0;
cursor: pointer;
transition: all 0.2s ease;
text-transform: uppercase;
letter-spacing: 2px;
margin-top: 0.5rem;
box-sizing: border-box;
&:hover {
background: #ffffff;
color: #000000;
}
&:active {
background: #333333;
color: #ffffff;
border-color: #cccccc;
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
border-color: #666666;
color: #666666;
}
`;
`
export const FormTitle = styled.h1`
font-size: 1.4rem;
font-weight: 700;
font-family: 'Courier New', monospace;
color: #ffffff;
text-align: center;
margin: 0 0 0.5rem 0;
text-transform: uppercase;
letter-spacing: 3px;
`;
export const FormSubtitle = styled.p`
font-size: 0.75rem;
font-family: 'Courier New', monospace;
color: #999999;
text-align: center;
margin: 0 0 1.5rem 0;
font-weight: 400;
line-height: 1.5;
text-transform: uppercase;
letter-spacing: 1px;
`;
export const InputContainer = styled.div`
width: 100%;
`;
export const ErrorMessage = styled.div`
color: #ffffff;
font-size: 0.8rem;
font-weight: 400;
font-family: 'Courier New', monospace;
text-align: center;
padding: 0.75rem 1rem;
background: #000000;
border: 1px solid #333333;
border-radius: 0;
margin: 0.5rem 0;
text-transform: uppercase;
letter-spacing: 1px;
animation: ${fadeIn} 0.3s ease-out;
`;

View File

@@ -1,11 +1,13 @@
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700;800;900&display=swap');
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-family: 'JetBrains Mono', 'Courier New', monospace;
line-height: 1.4;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
color-scheme: dark;
color: #ffffff;
background-color: #000000;
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -13,56 +15,142 @@
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
a:hover {
color: #535bf2;
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
background-color: #000000;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
padding: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
background:
radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.01) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.01) 0%, transparent 50%),
linear-gradient(135deg, #000000 0%, #0a0a0a 50%, #000000 100%);
background-attachment: fixed;
}
#root {
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow-x: hidden;
}
a {
font-weight: 600;
color: #ffffff;
text-decoration: none;
text-transform: uppercase;
letter-spacing: 1px;
transition: all 0.2s ease;
}
a:hover {
color: #cccccc;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.3);
}
h1, h2, h3, h4, h5, h6 {
font-family: 'JetBrains Mono', 'Courier New', monospace;
font-weight: 700;
color: #ffffff;
margin: 0;
text-transform: uppercase;
letter-spacing: 1px;
}
h1 {
font-size: 3.2em;
font-size: 2.5rem;
line-height: 1.1;
font-weight: 900;
letter-spacing: 3px;
}
p {
font-family: 'JetBrains Mono', 'Courier New', monospace;
color: #cccccc;
line-height: 1.5;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
font-family: 'JetBrains Mono', 'Courier New', monospace;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
input, textarea {
font-family: 'JetBrains Mono', 'Courier New', monospace;
}
/* Scrollbar styling for post-industrial feel */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #000000;
border: 1px solid #333333;
}
::-webkit-scrollbar-thumb {
background: #666666;
border: 1px solid #000000;
}
::-webkit-scrollbar-thumb:hover {
background: #888888;
}
/* Selection styling */
::selection {
background: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
::-moz-selection {
background: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
/* Focus styling */
:focus-visible {
outline: 2px solid #ffffff;
outline-offset: 2px;
}
/* Utility classes */
.text-center {
text-align: center;
}
.text-uppercase {
text-transform: uppercase;
}
.font-mono {
font-family: 'JetBrains Mono', 'Courier New', monospace;
}
.font-weight-bold {
font-weight: 700;
}
.letter-spacing {
letter-spacing: 1px;
}

View File

@@ -1,14 +1,39 @@
import styled from 'styled-components';
import {LoginForm} from '../../components';
const FlexContainer = styled.div`
width: 100%;
height: 100vh;
margin: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
background: #000000;
background-image:
linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px),
linear-gradient(180deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
background-size: 50px 50px;
overflow: hidden;
`;
const LoginFormContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
`;
export function LoginView() {
const handleLoginSubmit = (username: string, secret: string) => {
console.log('handling login submit username [%o] secret [%o]', username, secret)
};
console.log('LoginView...')
return <>
<LoginForm onSubmit={handleLoginSubmit} />
</>
return <FlexContainer>
<LoginFormContainer>
<LoginForm onSubmit={handleLoginSubmit} />
</LoginFormContainer>
</FlexContainer>
}