feat(login): new login screen design

This commit is contained in:
trashtemp 2021-12-25 16:01:19 +01:00
parent d7f52fb6f4
commit 5c59f187bd
No known key found for this signature in database
GPG key ID: D1F0DB65081B0FC6
9 changed files with 300 additions and 114 deletions

2
external/lang vendored

@ -1 +1 @@
Subproject commit 697960e42a5e27947a1b8d7338b406ae5a9372dd Subproject commit 0bac1d588e7bdf47cdc5659abbbbc75dfb75fd37

View file

@ -46,7 +46,13 @@ $fontSize: 40px;
@while ($count > 0) { @while ($count > 0) {
$left: random(100); $left: random(100);
$deltaLeft: random(2 * $windNoise * 10) / 10 - $windNoise + $windSpeed; $deltaLeft: random(2 * $windNoise * 10) / 10 - $windNoise + $windSpeed;
/*$deltaLeft: math.div(random(2 * $windNoise * 10), 10) -
$windNoise +
$windSpeed;*/
$scale: 1 + (random(2 * $sizeNoise * 10) / 10 - $sizeNoise) / 100; $scale: 1 + (random(2 * $sizeNoise * 10) / 10 - $sizeNoise) / 100;
/*$scale: 1 +
(math.div(random(2 * $sizeNoise * 10), 10) - $sizeNoise) /
100;*/
.snowflake:nth-child(#{$count}) { .snowflake:nth-child(#{$count}) {
animation: animation-snowflake-#{$count} linear infinite; animation: animation-snowflake-#{$count} linear infinite;

View file

@ -1,78 +1,168 @@
.login { .login {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 40px 35px;
display: flex; display: flex;
flex-direction: row; user-select: none;
svg { background: url("/src/pages/login/background.jpg");
margin: auto; background-position: center;
background-repeat: no-repeat;
background-size: cover;
.nav {
display: flex;
align-items: center;
.logo {
flex-grow: 1;
> img {
height: 24px;
}
} }
> div {
flex: 1;
} }
.content { .content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
flex-grow: 1;
}
.attribution { select {
color: var(--tertiary-background); max-width: 180px;
font-size: 12px; padding: 4px;
line-height: 12px; height: fit-content;
margin: 8px; background-color: rgba(36, 36, 36, 0.75);
border: 2px solid rgba(128, 128, 128, 0.15);
backdrop-filter: blur(20px);
font-size: 13px;
}
.middle {
display: flex; display: flex;
flex-direction: row; gap: 50px;
justify-content: space-between;
}
.modal { .loginQR {
display: flex; background-color: rgba(36, 36, 36, 0.75);
flex-direction: row; border: 2px solid rgba(128, 128, 128, 0.15);
border-radius: 8px;
justify-content: center; width: 330px;
}
}
.bg {
background-size: cover !important;
} }
} }
.form { .form {
background-color: rgba(36, 36, 36, 0.75);
border: 2px solid rgba(128, 128, 128, 0.15);
backdrop-filter: blur(20px);
max-width: 330px;
max-height: 600px;
padding: 30px 25px;
border-radius: 8px;
margin-inline-start: 50px;
margin-top: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 10px 0 rgb(0 0 0 / 20%);
.formModal {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 14px; gap: 10px;
img { > span {
width: 260px; font-size: 13px;
margin: auto;
b {
font-weight: 600;
}
} }
a { .welcome {
margin-top: 4px; font-size: 20px;
font-weight: 600;
margin-bottom: 10px;
}
.subtitle {
font-weight: 400;
font-size: 14px;
} }
form { form {
margin: 1em 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px;
> div {
margin: 0;
margin-bottom: -4px;
font-size: 12px;
color: var(--secondary-foreground);
}
input {
background: var(--secondary-background);
/*border: 2px solid rgba(128, 128, 128, 0.15);*/
}
button { button {
margin-top: 24px; padding: 8px;
background: var(--secondary-background);
margin-bottom: 15px;
}
}
} }
} }
.create {
text-align: center;
color: var(--tertiary-foreground);
a { a {
margin: 0 4px; color: white;
&:hover {
text-decoration: underline;
}
}
.bottom {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
.links {
display: flex;
align-items: center;
gap: 15px;
.socials {
display: flex;
gap: 10px;
a {
transition: opacity 0.2s ease-in-out;
&:hover {
opacity: 0.7;
}
&:active {
opacity: 0.5;
}
}
}
.bullet {
height: 5px;
width: 5px;
background: grey;
border-radius: 50%;
}
.revolt {
display: flex;
gap: 10px;
a {
color: white !important;
}
}
} }
} }
} }
@ -103,24 +193,46 @@
} }
} }
.footer {
margin-top: 12px;
text-align: center;
color: var(--tertiary6);
a {
color: var(--tertiary-background) !important;
cursor: pointer;
margin: 0 2px;
&:hover {
color: var(--tertiary-foreground) !important;
}
}
}
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
.bg { .login {
padding: 30px 20px;
background-image: unset;
background-color: var(--primary-background);
.content {
.nav {
.logo {
img {
height: 24px;
}
}
}
.bottom {
flex-direction: column;
.links {
flex-direction: column;
gap: 10px;
}
.bullet,
.attribution {
display: none; display: none;
} }
} }
}
.form {
border: unset;
margin-inline-start: 0;
padding: 0;
max-width: unset;
box-shadow: unset;
}
.loginQR {
display: none;
}
}
}

View file

@ -1,3 +1,4 @@
import { Twitter, Github, Mastodon } from "@styled-icons/boxicons-logos";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import { Route, Switch } from "react-router-dom"; import { Route, Switch } from "react-router-dom";
@ -8,6 +9,7 @@ import { Text } from "preact-i18n";
import { useApplicationState } from "../../mobx/State"; import { useApplicationState } from "../../mobx/State";
import wideSVG from "../../../public/assets/wide.svg";
import LocaleSelector from "../../components/common/LocaleSelector"; import LocaleSelector from "../../components/common/LocaleSelector";
import background from "./background.jpg"; import background from "./background.jpg";
@ -36,14 +38,104 @@ export default observer(() => {
/> />
</Helmet> </Helmet>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.attribution}> <div className={styles.nav}>
<a className={styles.logo}>
<img
src="https://app.revolt.chat/assets/wide.fe712d4a.svg"
draggable={false}
/>
</a>
<LocaleSelector />
</div>
{/*<div className={styles.middle}>*/}
<div className={styles.form}>
{/*<div style={styles.version}>
API: <code>{configuration?.revolt ?? "???"}</code>{" "}
&middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "}
&middot; App: <code>{APP_VERSION}</code>
</div>*/}
<Switch>
<Route path="/login/create">
<FormCreate />
</Route>
<Route path="/login/resend">
<FormResend />
</Route>
<Route path="/login/verify/:token">
<FormVerify />
</Route>
<Route path="/login/reset/:token">
<FormReset />
</Route>
<Route path="/login/reset">
<FormSendReset />
</Route>
<Route path="/">
<FormLogin />
</Route>
</Switch>
</div>
{/*<div className={styles.loginQR}></div>*/}
{/*</div>*/}
<div className={styles.bottom}>
<div className={styles.links}>
<div className={styles.socials}>
<a
href="https://github.com/revoltchat"
target="_blank">
<Github size={24} />
</a>
<a
href="https://twitter.com/revoltchat"
target="_blank">
<Twitter size={24} />
</a>
<a
href="https://mastodon.social/@revoltchat"
target="_blank">
<Mastodon size={24} />
</a>
</div>
<div className={styles.bullet} />
<div className={styles.revolt}>
<a
href="https://revolt.chat/about"
target="_blank"
rel="noreferrer">
<Text id="general.about" />
</a>
<a
href="https://revolt.chat/terms"
target="_blank"
rel="noreferrer">
<Text id="general.tos" />
</a>
<a
href="https://revolt.chat/privacy"
target="_blank"
rel="noreferrer">
<Text id="general.privacy" />
</a>
</div>
</div>
<a
className={styles.attribution}
href="https://unsplash.com/@fakurian"
target="_blank">
<Text id="general.image_by" /> &lrm;@fakurian &rlm;·
unsplash.com
</a>
</div>
{/*<div className={styles.attribution}>
<span> <span>
API: <code>{configuration?.revolt ?? "???"}</code>{" "} API: <code>{configuration?.revolt ?? "???"}</code>{" "}
&middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "} &middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "}
&middot; App: <code>{APP_VERSION}</code> &middot; App: <code>{APP_VERSION}</code>
</span> </span>
<span> <span>
<LocaleSelector />
</span> </span>
</div> </div>
<div className={styles.modal}> <div className={styles.modal}>
@ -67,18 +159,8 @@ export default observer(() => {
<FormLogin /> <FormLogin />
</Route> </Route>
</Switch> </Switch>
</div>*/}
</div> </div>
<div className={styles.attribution}>
<span>
<Text id="general.image_by" /> &lrm;@lorenzoherrera
&rlm;· unsplash.com
</span>
</div>
</div>
<div
className={styles.bg}
style={{ background: `url('${background}')` }}
/>
</div> </div>
</> </>
); );

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -18,7 +18,6 @@ import Preloader from "../../../components/ui/Preloader";
import FormField from "../FormField"; import FormField from "../FormField";
import { CaptchaBlock, CaptchaProps } from "./CaptchaBlock"; import { CaptchaBlock, CaptchaProps } from "./CaptchaBlock";
import { Legal } from "./Legal";
import { MailProvider } from "./MailProvider"; import { MailProvider } from "./MailProvider";
interface Props { interface Props {
@ -144,8 +143,16 @@ export function Form({ page, callback }: Props) {
if (loading) return <Preloader type="spinner" />; if (loading) return <Preloader type="spinner" />;
return ( return (
<div className={styles.form}> <div className={styles.formModal}>
<img src={wideSVG} /> <div className={styles.welcome}>
<div className={styles.title}>
<Text id="login.welcome" />
</div>
<div className={styles.subtitle}>
<Text id="login.subtitle" />
</div>
</div>
{/* Preact / React typing incompatabilities */} {/* Preact / React typing incompatabilities */}
<form <form
onSubmit={ onSubmit={
@ -203,13 +210,17 @@ export function Form({ page, callback }: Props) {
{page === "create" && ( {page === "create" && (
<> <>
<span className={styles.create}> <span className={styles.create}>
<b>
<Text id="login.existing" /> <Text id="login.existing" />
</b>{" "}
<Link to="/login"> <Link to="/login">
<Text id="login.title" /> <Text id="login.title" />
</Link> </Link>
</span> </span>
<span className={styles.create}> <span className={styles.create}>
<b>
<Text id="login.missing_verification" /> <Text id="login.missing_verification" />
</b>{" "}
<Link to="/login/resend"> <Link to="/login/resend">
<Text id="login.resend" /> <Text id="login.resend" />
</Link> </Link>
@ -219,13 +230,17 @@ export function Form({ page, callback }: Props) {
{page === "login" && ( {page === "login" && (
<> <>
<span className={styles.create}> <span className={styles.create}>
<b>
<Text id="login.new" /> <Text id="login.new" />
</b>{" "}
<Link to="/login/create"> <Link to="/login/create">
<Text id="login.create" /> <Text id="login.create" />
</Link> </Link>
</span> </span>
<span className={styles.create}> <span className={styles.create}>
<b>
<Text id="login.forgot" /> <Text id="login.forgot" />
</b>{" "}
<Link to="/login/reset"> <Link to="/login/reset">
<Text id="login.reset" /> <Text id="login.reset" />
</Link> </Link>
@ -243,7 +258,6 @@ export function Form({ page, callback }: Props) {
</span> </span>
</> </>
)} )}
<Legal />
</div> </div>
); );
} }

View file

@ -1,29 +0,0 @@
import styles from "../Login.module.scss";
import { Text } from "preact-i18n";
export function Legal() {
return (
<span className={styles.footer}>
<a
href="https://revolt.chat/about"
target="_blank"
rel="noreferrer">
<Text id="general.about" />
</a>
&middot;
<a
href="https://revolt.chat/terms"
target="_blank"
rel="noreferrer">
<Text id="general.tos" />
</a>
&middot;
<a
href="https://revolt.chat/privacy"
target="_blank"
rel="noreferrer">
<Text id="general.privacy" />
</a>
</span>
);
}

View file

@ -126,6 +126,7 @@ export const Overview = observer(({ server }: Props) => {
alignItems: "center", alignItems: "center",
}}> }}>
<span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span> <span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
<span>Sends a message when someone joins your server</span>
<ComboBox <ComboBox
value={ value={
systemMessages?.[ systemMessages?.[