mirror of
https://github.com/logto-io/logto.git
synced 2025-02-03 21:48:55 -05:00
feat(ui): ui style foundation update (#583)
* feat(ui): ui style foundation update ui style foundation update * fix(ui): remove legacy style remove legacy style * refactor(ui): remove errorMessage shrink space logic remove errorMessage shrink space logic
This commit is contained in:
parent
4491eab5b4
commit
93a93b4c8f
49 changed files with 263 additions and 310 deletions
|
@ -1,6 +1,6 @@
|
|||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol width="18" height="18" viewBox="0 0 18 18" id="unchecked">
|
||||
<circle cx="9" cy="9" r="8" stroke="#D8D8D8" stroke-width="2" fill="transparent"/>
|
||||
<circle cx="9" cy="9" r="8" stroke="#D8D8D8" stroke-width="2" fill="transparent" />
|
||||
</symbol>
|
||||
<symbol width="18" height="18" viewBox="0 0 18 18" id="checked">
|
||||
<circle cx="9" cy="9" r="9" fill="#6139F6"/>
|
||||
|
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 791 B |
|
@ -1,87 +1,55 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
/* Foundation */
|
||||
$color-neutral-100: #111;
|
||||
$color-neutral-90: #666;
|
||||
$color-neutral-70: #999;
|
||||
$color-neutral-50: #aeaeae;
|
||||
$color-neutral-30: #d8d8d8;
|
||||
$color-neutral-10: #f4f4f4;
|
||||
$color-neutral-0: #fff;
|
||||
|
||||
$color-primary: #6139f6;
|
||||
$color-primary-tint-60: #a48dfa;
|
||||
$color-primary-tint-70: #b09bfa;
|
||||
|
||||
$font-family: 'PingFang SC', 'SF Pro Text', sans-serif;
|
||||
$font-family: 'PingFang SC', 'SF Pro Display', 'Siyuan Heiti', 'Roboto';
|
||||
$font-family-small: 'PingFang SC', 'SF Pro Text', 'Siyuan Heiti', 'Roboto';
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-background);
|
||||
color: var(--color-font-primary);
|
||||
background: var(--color-base);
|
||||
color: var(--color-text);
|
||||
font: var(--font-body);
|
||||
}
|
||||
|
||||
.universal {
|
||||
--color-error: #ea0000;
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
.light {
|
||||
--color-text: #191c1d;
|
||||
--color-icon: #747778;
|
||||
--color-caption: #747778;
|
||||
--color-outline: #78767f;
|
||||
--color-border: #e0e3e3;
|
||||
--color-disabled: #c4c7c7;
|
||||
--color-primary: #5d34f2;
|
||||
--color-layer: #eff1f1;
|
||||
--color-error: #ba1b1b;
|
||||
--color-toast: rgba(25, 28, 29, 80%);
|
||||
--color-overlay: rgba(25, 28, 29, 16%);
|
||||
--color-base: #fff;
|
||||
}
|
||||
|
||||
.light,
|
||||
.dark {
|
||||
/* Color */
|
||||
--color-primary: #{$color-primary};
|
||||
--color-background: #{$color-neutral-0};
|
||||
--color-secondary-background-active: #{$color-neutral-10};
|
||||
--color-secondary-background-disabled: #{$color-neutral-10};
|
||||
--color-border: #{$color-neutral-100};
|
||||
--color-border-secondary: #{$color-neutral-70};
|
||||
--color-border-disabled: #{$color-neutral-30};
|
||||
--color-control-background: #{$color-neutral-10};
|
||||
--color-control-focus: #{$color-primary-tint-60};
|
||||
--color-control-action: #{$color-neutral-50};
|
||||
--color-checkbox-border: #{$color-neutral-30};
|
||||
--color-divider: #dbdbdb;
|
||||
--color-dark-background: #{rgba($color-neutral-100, 0.8)};
|
||||
--color-loading-layer: rgba(0, 0, 0, 8%);
|
||||
|
||||
/* Font Color */
|
||||
--color-font-primary: #{$color-neutral-100};
|
||||
--color-font-secondary: #444;
|
||||
--color-font-tertiary: #777;
|
||||
--color-font-placeholder: #aaa;
|
||||
--color-font-divider: #bbb;
|
||||
--color-font-button-text: #{$color-neutral-0};
|
||||
--color-font-button-text-active: #{rgba($color-neutral-0, 0.4)};
|
||||
--color-font-secondary-dialog: #{$color-neutral-70};
|
||||
--color-font-secondary-disabled: #{rgba($color-neutral-100, 0.4)};
|
||||
--color-font-link: #{$color-primary};
|
||||
--color-font-link-secondary: #{$color-neutral-70};
|
||||
--color-font-toast-text: #{$color-neutral-0};
|
||||
|
||||
/* ===== Legacy Styling ===== */
|
||||
--color-heading: #333;
|
||||
--color-body: #555;
|
||||
--color-secondary: #888;
|
||||
--color-placeholder: #aaa;
|
||||
--color-control-background-disabled: #eaeaea;
|
||||
|
||||
/* Shadow */
|
||||
--shadow-card: 2px 2px 24px rgb(187, 189, 191, 20%);
|
||||
--shadow-control: 1px 1px 2px rgb(221, 221, 221, 25%);
|
||||
--color-text: #f7f8f8;
|
||||
--color-icon: #a9acac;
|
||||
--color-caption: #a9acac;
|
||||
--color-outline: #928f9a;
|
||||
--color-border: #444748;
|
||||
--color-disabled: #5c5f60;
|
||||
--color-primary: #7958ff;
|
||||
--color-layer: linear-gradient(0deg, rgba(202, 190, 255, 14%), rgba(202, 190, 255, 14%)), linear-gradient(0deg, rgba(196, 199, 199, 2%), rgba(196, 199, 199, 2%)), #191c1d;
|
||||
--color-error: #dd3730;
|
||||
--color-toast: rgba(247, 248, 248, 80%);
|
||||
--color-overlay: rgba(247, 248, 248, 80%);
|
||||
--color-base: #191c1d;
|
||||
}
|
||||
|
||||
.mobile {
|
||||
--font-title: 600 32px/40px #{$font-family};
|
||||
--font-heading-2: 500 18px/22px #{$font-family};
|
||||
--font-heading-2-bold: 600 18px/22px #{$font-family};
|
||||
--font-control: 500 18px/20px #{$font-family};
|
||||
--font-button-text: 600 20px/24px #{$font-family};
|
||||
--font-button-text-small: 500 18px/22px #{$font-family};
|
||||
--font-body-bold: 500 16px/20px #{$font-family};
|
||||
--font-body: 400 16px/20px #{$font-family};
|
||||
--font-body-bold: 600 16px/20px #{$font-family};
|
||||
--font-body-small: 400 14px/18px #{$font-family};
|
||||
/* ===== Legacy Styling ===== */
|
||||
--font-headline: 600 40px/56px #{$font-family};
|
||||
--font-heading-1: 600 28px/39px #{$font-family};
|
||||
--font-heading-3: 600 16px/22.4px #{$font-family};
|
||||
--font-body-small: 500 14px/18px #{$font-family-small};
|
||||
--font-caption: 400 14px/18px #{$font-family-small};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ $logo-height: 60px;
|
|||
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 15vh;
|
||||
min-height: 92px;
|
||||
max-height: 148px;
|
||||
@include _.flex-column;
|
||||
}
|
||||
|
||||
|
@ -18,6 +21,6 @@ $logo-height: 60px;
|
|||
}
|
||||
|
||||
.headline {
|
||||
font: var(--font-heading-2-bold);
|
||||
color: var(--color-font-secondary);
|
||||
font: var(--font-body);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './SocialIconButton.module.scss';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const MoreButton = ({ className, onClick }: Props) => {
|
||||
return (
|
||||
<button className={classNames(styles.socialButton, styles.more, className)} onClick={onClick} />
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreButton;
|
|
@ -6,12 +6,8 @@
|
|||
height: 48px;
|
||||
border-radius: 50%;
|
||||
@include _.flex-column;
|
||||
background: var(--color-secondary-background-active);
|
||||
background: var(--color-layer);
|
||||
border: none;
|
||||
|
||||
&.more {
|
||||
background: url('../../assets/icons/more-social-icon.svg') no-repeat center;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.socialButton {
|
||||
font: var(--font-control);
|
||||
border: _.border(var(--color-border));
|
||||
background: var(--color-background);
|
||||
color: var(--color-font-primary);
|
||||
justify-content: flex-start;
|
||||
border: _.border(var(--color-outline));
|
||||
background: var(--color-base);
|
||||
color: var(--color-text);
|
||||
|
||||
.icon {
|
||||
width: _.unit(5);
|
||||
height: _.unit(5);
|
||||
width: _.unit(6);
|
||||
height: _.unit(6);
|
||||
@include _.image-align-center;
|
||||
margin-right: _.unit(4);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import classNames from 'classnames';
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import * as SocialLinkButtonStyles from './SocialLinkButton.module.scss';
|
||||
import * as socialLinkButtonStyles from './SocialLinkButton.module.scss';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type Props = {
|
||||
|
@ -24,13 +24,18 @@ const SocialLinkButton = ({ isDisabled, className, connector, onClick }: Props)
|
|||
return (
|
||||
<button
|
||||
disabled={isDisabled}
|
||||
className={classNames(styles.button, SocialLinkButtonStyles.socialButton, className)}
|
||||
className={classNames(
|
||||
styles.button,
|
||||
isDisabled && styles.disabled,
|
||||
socialLinkButtonStyles.socialButton,
|
||||
className
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onClick?.(id);
|
||||
}}
|
||||
>
|
||||
{logo && <img src={logo} alt={localName} className={SocialLinkButtonStyles.icon} />}
|
||||
{logo && <img src={logo} alt={localName} className={socialLinkButtonStyles.icon} />}
|
||||
{localName}
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -1,37 +1,38 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@include _.flex-row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: _.unit(3);
|
||||
border-radius: _.unit(2);
|
||||
font: var(--font-button-text);
|
||||
transition: var(--transition-default-control);
|
||||
border-radius: var(--radius);
|
||||
font: var(--font-body-bold);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.primary {
|
||||
border: none;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-font-button-text);
|
||||
color: var(--color-base);
|
||||
|
||||
&.disabled {
|
||||
background: var(--color-layer);
|
||||
color: var(--color-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary {
|
||||
border: _.border(var(--color-border));
|
||||
background: var(--color-background);
|
||||
color: var(--color-font-primary);
|
||||
border: _.border(var(--color-outline));
|
||||
background: var(--color-base);
|
||||
color: var(--color-text);
|
||||
|
||||
&.disabled {
|
||||
color: var(--color-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
font: var(--font-button-text-small);
|
||||
|
||||
&.secondary {
|
||||
border: _.border(var(--color-border-secondary));
|
||||
color: var(--color-font-secondary-dialog);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as styles from './index.module.scss';
|
|||
|
||||
export type Props = {
|
||||
htmlType?: 'button' | 'submit' | 'reset';
|
||||
size?: 'large' | 'small';
|
||||
size?: 'regular' | 'small';
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
children: ReactNode; // TODO: make it i18nKey with optional params
|
||||
|
@ -16,7 +16,7 @@ export type Props = {
|
|||
const Button = ({
|
||||
htmlType = 'button',
|
||||
type = 'primary',
|
||||
size = 'large',
|
||||
size = 'regular',
|
||||
isDisabled,
|
||||
className,
|
||||
children,
|
||||
|
@ -24,7 +24,13 @@ const Button = ({
|
|||
}: Props) => (
|
||||
<button
|
||||
disabled={isDisabled}
|
||||
className={classNames(styles.button, styles[type], styles[size], className)}
|
||||
className={classNames(
|
||||
styles.button,
|
||||
styles[type],
|
||||
styles[size],
|
||||
isDisabled && styles.disabled,
|
||||
className
|
||||
)}
|
||||
type={htmlType}
|
||||
onClick={onClick}
|
||||
>
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
background: var(--color-background);
|
||||
padding: _.unit(6);
|
||||
border-radius: _.unit(2);
|
||||
background: var(--color-base);
|
||||
padding: _.unit(6) _.unit(5);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
font: var(--font-body-medium);
|
||||
color: var(--color-font-primary);
|
||||
font: var(--font-body);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.footer {
|
||||
@include _.flex_row;
|
||||
margin-top: _.unit(6);
|
||||
justify-content: space-between;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
|
||||
.divider {
|
||||
@include _.flex-row;
|
||||
font: var(--font-body-medium);
|
||||
color: var(--color-font-divider);
|
||||
font: var(--font-body);
|
||||
color: var(--color-caption);
|
||||
margin: _.unit(4) 0;
|
||||
width: 100%;
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--color-divider);
|
||||
background: var(--color-border);
|
||||
|
||||
&:first-child {
|
||||
margin-right: _.unit(4);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.container {
|
||||
padding: _.unit(5);
|
||||
background: var(--color-background);
|
||||
background: var(--color-base);
|
||||
}
|
||||
|
||||
.header {
|
||||
|
@ -11,3 +11,7 @@
|
|||
align-items: center;
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
fill: var(--color-icon);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ const Drawer = ({ className, isOpen = false, children, onClose }: Props) => {
|
|||
>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>
|
||||
<ClearIcon onClick={onClose} />
|
||||
<ClearIcon className={styles.closeIcon} onClick={onClose} />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.error {
|
||||
font: var(--font-body-small);
|
||||
font: var(--font-caption);
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
|
11
packages/ui/src/components/Icons/MoreSocialIcon.tsx
Normal file
11
packages/ui/src/components/Icons/MoreSocialIcon.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React, { SVGProps } from 'react';
|
||||
|
||||
import More from '@/assets/icons/more-social-icon.svg';
|
||||
|
||||
const MoreSocialIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<use href={`${More}#more`} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MoreSocialIcon;
|
|
@ -4,10 +4,10 @@
|
|||
position: relative;
|
||||
@include _.flex-row;
|
||||
padding: 0 _.unit(4);
|
||||
border-radius: _.unit(2);
|
||||
border-radius: var(--radius);
|
||||
border: _.border();
|
||||
background: var(--color-control-background);
|
||||
color: var(--color-font-primary);
|
||||
background: var(--color-layer);
|
||||
color: var(--color-text);
|
||||
|
||||
|
||||
> *:not(:first-child) {
|
||||
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
|
||||
&.focus {
|
||||
border: _.border(var(--color-control-focus));
|
||||
border: _.border(var(--color-primary));
|
||||
}
|
||||
|
||||
&.error {
|
||||
|
@ -28,13 +28,13 @@
|
|||
background: none;
|
||||
padding: _.unit(3) 0;
|
||||
caret-color: var(--color-primary);
|
||||
font: var(--font-control);
|
||||
transition: var(--transition-default-control);
|
||||
font: var(--font-body-bold);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-placeholder);
|
||||
color: var(--color-caption);
|
||||
}
|
||||
|
||||
// Overwrite webkit auto-fill style
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0 30px var(--color-control-background) inset;
|
||||
transition: background-color 5000s ease-in-out 0s;
|
||||
|
@ -47,5 +47,5 @@
|
|||
}
|
||||
|
||||
.actionButton {
|
||||
fill: var(--color-control-action);
|
||||
fill: var(--color-icon);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.countryCodeSelector {
|
||||
color: var(--color-font-primary);
|
||||
font: var(--font-control);
|
||||
color: var(--color-text);
|
||||
font: var(--font-body);
|
||||
border: none;
|
||||
background: none;
|
||||
width: auto;
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--color-loading-layer);
|
||||
background-color: var(--color-overlay);
|
||||
@include _.flex-column;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-dark-background);
|
||||
border-radius: var(--radius);
|
||||
background-color: var(--color-toast);
|
||||
@include _.flex-column;
|
||||
}
|
||||
|
||||
|
|
11
packages/ui/src/components/NavBar/index.module.scss
Normal file
11
packages/ui/src/components/NavBar/index.module.scss
Normal file
|
@ -0,0 +1,11 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.navBar {
|
||||
width: 100%;
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
svg {
|
||||
margin-left: _.unit(-2);
|
||||
fill: var(--color-icon);
|
||||
}
|
||||
}
|
21
packages/ui/src/components/NavBar/index.tsx
Normal file
21
packages/ui/src/components/NavBar/index.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { NavArrowIcon } from '../Icons';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const NavBar = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className={styles.navBar}>
|
||||
<NavArrowIcon
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavBar;
|
|
@ -4,18 +4,19 @@
|
|||
@include _.flex-row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
max-width: 375px;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
|
||||
input {
|
||||
border-radius: _.unit(2);
|
||||
border: _.border();
|
||||
background: var(--color-control-background);
|
||||
background: var(--color-layer);
|
||||
caret-color: var(--color-primary);
|
||||
width: _.unit(12);
|
||||
height: _.unit(12);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
text-align: center;
|
||||
font: var(--font-control);
|
||||
color: var(--color-font-primary);
|
||||
font: var(--font-body);
|
||||
color: var(--color-text);
|
||||
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
|
@ -24,7 +25,7 @@
|
|||
}
|
||||
|
||||
&:focus {
|
||||
border: _.border(var(--color-control-focus));
|
||||
border: _.border(var(--color-primary));
|
||||
}
|
||||
|
||||
&.error {
|
||||
|
@ -32,7 +33,7 @@
|
|||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-font-placeholder);
|
||||
color: var(--color-caption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
.terms {
|
||||
@include _.flex-row;
|
||||
width: 100%;
|
||||
|
||||
input[type='checkbox'] {
|
||||
appearance: none;
|
||||
|
@ -14,11 +15,15 @@
|
|||
|
||||
.radioButton {
|
||||
margin-right: _.unit(2);
|
||||
transform: scale(0.8);
|
||||
fill: var(--color-icon);
|
||||
}
|
||||
|
||||
.content {
|
||||
@include _.text-hint;
|
||||
|
||||
.link {
|
||||
@include _.text-hint;
|
||||
}
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
|
|
|
@ -39,6 +39,7 @@ const TermsOfUse = ({ name, className, termsOfUse, isChecked, error, onChange }:
|
|||
<div className={styles.content}>
|
||||
{prefix}
|
||||
<TextLink
|
||||
className={styles.link}
|
||||
text="description.terms_of_use"
|
||||
href={termsOfUse.contentUrl}
|
||||
type="secondary"
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.link {
|
||||
transition: var(--transition-default-control);
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&.primary {
|
||||
color: var(--color-font-link);
|
||||
color: var(--color-primary);
|
||||
font: var(--font-body-bold);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: var(--color-font-link-secondary);
|
||||
font: var(--font-body-small);
|
||||
color: var(--color-caption);
|
||||
font: var(--font-body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,16 @@
|
|||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
@include _.flex-column;
|
||||
padding: 0 _.unit(8);
|
||||
pointer-events: none;
|
||||
|
||||
.toast {
|
||||
max-width: 360px;
|
||||
max-width: 295px;
|
||||
margin: 0 auto;
|
||||
padding: _.unit(2) _.unit(4);
|
||||
font: var(--font-body-medium);
|
||||
color: var(--color-font-toast-text);
|
||||
border-radius: _.unit(2);
|
||||
background: var(--color-dark-background);
|
||||
min-width: _.unit(25);
|
||||
font: var(--font-body);
|
||||
color: var(--color-base);
|
||||
border-radius: var(--radius);
|
||||
background: var(--color-toast);
|
||||
text-align: center;
|
||||
opacity: 0%;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.form {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
@include _.flex-column;
|
||||
|
||||
> * {
|
||||
|
@ -11,21 +12,9 @@
|
|||
|
||||
.inputField {
|
||||
margin-bottom: _.unit(4);
|
||||
|
||||
&.withError {
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
.terms {
|
||||
margin: _.unit(7) 0 _.unit(6);
|
||||
|
||||
&.withError {
|
||||
margin: _.unit(7) 0 _.unit(2) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.inputField.withError + .terms {
|
||||
margin-top: _.unit(9);
|
||||
margin: _.unit(8) 0 _.unit(4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 3. Forgot password URL
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
|
||||
|
@ -36,6 +35,10 @@ type FieldValidations = {
|
|||
[key in keyof FieldState]?: (state: FieldState) => ErrorType | undefined;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const defaultState = {
|
||||
username: '',
|
||||
password: '',
|
||||
|
@ -45,7 +48,7 @@ const defaultState = {
|
|||
|
||||
const usernameRegx = /^[A-Z_a-z-][\w-]*$/;
|
||||
|
||||
const CreateAccount = () => {
|
||||
const CreateAccount = ({ className }: Props) => {
|
||||
const { t, i18n } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
|
@ -157,9 +160,9 @@ const CreateAccount = () => {
|
|||
}, [error, i18n, setToast, t]);
|
||||
|
||||
return (
|
||||
<form className={styles.form}>
|
||||
<form className={classNames(styles.form, className)}>
|
||||
<Input
|
||||
className={classNames(styles.inputField, fieldErrors.username && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="username"
|
||||
autoComplete="username"
|
||||
placeholder={t('input.username')}
|
||||
|
@ -177,7 +180,7 @@ const CreateAccount = () => {
|
|||
/>
|
||||
<PasswordInput
|
||||
forceHidden
|
||||
className={classNames(styles.inputField, fieldErrors.password && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="password"
|
||||
autoComplete="current-password"
|
||||
placeholder={t('input.password')}
|
||||
|
@ -192,7 +195,7 @@ const CreateAccount = () => {
|
|||
/>
|
||||
<PasswordInput
|
||||
forceHidden
|
||||
className={classNames(styles.inputField, fieldErrors.confirmPassword && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="confirm_password"
|
||||
autoComplete="current-password"
|
||||
placeholder={t('input.confirm_password')}
|
||||
|
@ -207,7 +210,7 @@ const CreateAccount = () => {
|
|||
/>
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={classNames(styles.terms, fieldErrors.termsAgreement && styles.withError)}
|
||||
className={styles.terms}
|
||||
termsOfUse={{ enabled: true, contentUrl: '/' }}
|
||||
isChecked={fieldState.termsAgreement}
|
||||
error={fieldErrors.termsAgreement}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.form {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
@include _.flex-column;
|
||||
|
||||
> * {
|
||||
|
@ -17,7 +18,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
font: var(--font-caption);
|
||||
}
|
||||
|
||||
.message {
|
||||
font: var(--font-caption);
|
||||
color: var(--color-text);
|
||||
|
||||
> span {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ const PasscodeValidation = ({ type, method, className, target }: Props) => {
|
|||
renderCountDownMessage
|
||||
) : (
|
||||
<TextLink
|
||||
className={styles.link}
|
||||
text="description.resend_passcode"
|
||||
onClick={() => {
|
||||
void sendPassCode(target);
|
||||
|
|
|
@ -22,6 +22,7 @@ import * as styles from './index.module.scss';
|
|||
|
||||
type Props = {
|
||||
type: UserFlow;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type FieldState = {
|
||||
|
@ -41,7 +42,7 @@ const defaultState: FieldState = { email: '', termsAgreement: false };
|
|||
|
||||
const emailRegEx = /^\S+@\S+\.\S+$/;
|
||||
|
||||
const EmailPasswordless = ({ type }: Props) => {
|
||||
const EmailPasswordless = ({ type, className }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
|
@ -89,8 +90,6 @@ const EmailPasswordless = ({ type }: Props) => {
|
|||
}, [validations, fieldState, asyncSendPasscode]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(result);
|
||||
|
||||
if (result) {
|
||||
navigate(`/${type}/email/passcode-validation`, { state: { email: fieldState.email } });
|
||||
}
|
||||
|
@ -119,9 +118,9 @@ const EmailPasswordless = ({ type }: Props) => {
|
|||
}, [error, t, setToast]);
|
||||
|
||||
return (
|
||||
<form className={styles.form}>
|
||||
<form className={classNames(styles.form, className)}>
|
||||
<Input
|
||||
className={classNames(styles.inputField, fieldErrors.email && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
placeholder={t('input.email')}
|
||||
|
@ -140,7 +139,7 @@ const EmailPasswordless = ({ type }: Props) => {
|
|||
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={classNames(styles.terms, fieldErrors.termsAgreement && styles.withError)}
|
||||
className={styles.terms}
|
||||
termsOfUse={{ enabled: true, contentUrl: '/' }}
|
||||
isChecked={fieldState.termsAgreement}
|
||||
error={fieldErrors.termsAgreement}
|
||||
|
|
|
@ -23,6 +23,7 @@ import * as styles from './index.module.scss';
|
|||
|
||||
type Props = {
|
||||
type: UserFlow;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type FieldState = {
|
||||
|
@ -40,7 +41,7 @@ type FieldValidations = {
|
|||
|
||||
const defaultState: FieldState = { phone: '', termsAgreement: false };
|
||||
|
||||
const PhonePasswordless = ({ type }: Props) => {
|
||||
const PhonePasswordless = ({ type, className }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
|
@ -125,10 +126,10 @@ const PhonePasswordless = ({ type }: Props) => {
|
|||
}, [error, t, setToast]);
|
||||
|
||||
return (
|
||||
<form className={styles.form}>
|
||||
<form className={classNames(styles.form, className)}>
|
||||
<PhoneInput
|
||||
name="phone"
|
||||
className={classNames(styles.inputField, fieldErrors.phone && styles.withError)}
|
||||
className={styles.inputField}
|
||||
autoComplete="mobile"
|
||||
placeholder={t('input.phone_number')}
|
||||
countryCallingCode={phoneNumber.countryCallingCode}
|
||||
|
@ -141,7 +142,7 @@ const PhonePasswordless = ({ type }: Props) => {
|
|||
/>
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={classNames(styles.terms, fieldErrors.termsAgreement && styles.withError)}
|
||||
className={styles.terms}
|
||||
termsOfUse={{ enabled: true, contentUrl: '/' }}
|
||||
isChecked={fieldState.termsAgreement}
|
||||
error={fieldErrors.termsAgreement}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.form {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
@include _.flex-column;
|
||||
|
||||
> * {
|
||||
|
@ -11,17 +12,9 @@
|
|||
|
||||
.inputField {
|
||||
margin-bottom: _.unit(11);
|
||||
|
||||
&.withError {
|
||||
margin-bottom: _.unit(9);
|
||||
}
|
||||
}
|
||||
|
||||
.terms {
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
&.withError {
|
||||
margin-bottom: _.unit(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as styles from './index.module.scss';
|
|||
type Props = {
|
||||
signInMethods: LocalSignInMethod[];
|
||||
type?: 'primary' | 'secondary';
|
||||
classname?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const SignInMethodsKeyMap: {
|
||||
|
@ -23,7 +23,7 @@ const SignInMethodsKeyMap: {
|
|||
sms: 'phone_number',
|
||||
};
|
||||
|
||||
const SignInMethodsLink = ({ signInMethods, type = 'secondary', classname }: Props) => {
|
||||
const SignInMethodsLink = ({ signInMethods, type = 'secondary', className }: Props) => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
|
||||
|
@ -43,7 +43,7 @@ const SignInMethodsLink = ({ signInMethods, type = 'secondary', classname }: Pro
|
|||
);
|
||||
|
||||
if (type === 'primary') {
|
||||
return <div className={classNames(styles.methodsPrimary, classname)}>{signInMethodsLink}</div>;
|
||||
return <div className={classNames(styles.methodsPrimary, className)}>{signInMethodsLink}</div>;
|
||||
}
|
||||
|
||||
if (signInMethods.length > 1) {
|
||||
|
@ -58,13 +58,13 @@ const SignInMethodsLink = ({ signInMethods, type = 'secondary', classname }: Pro
|
|||
rawText
|
||||
);
|
||||
|
||||
return <div className={classNames(styles.methodsSecondary, classname)}>{textLink}</div>;
|
||||
return <div className={classNames(styles.methodsSecondary, className)}>{textLink}</div>;
|
||||
}
|
||||
|
||||
const rawText = t('secondary.sign_in_with', { method: signInMethods[0] });
|
||||
const textLink = reactStringReplace(rawText, signInMethods[0], () => signInMethodsLink[0]);
|
||||
|
||||
return <div className={classNames(styles.methodsSecondary, classname)}>{textLink}</div>;
|
||||
return <div className={classNames(styles.methodsSecondary, className)}>{textLink}</div>;
|
||||
};
|
||||
|
||||
export default SignInMethodsLink;
|
||||
|
|
|
@ -50,7 +50,8 @@ describe('SecondarySocialSignIn', () => {
|
|||
<SecondarySocialSignIn connectors={socialConnectors} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(container.querySelectorAll('button')).toHaveLength(4);
|
||||
expect(container.querySelectorAll('button')).toHaveLength(3);
|
||||
expect(container.querySelector('svg')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('invoke web social signIn', async () => {
|
||||
|
|
|
@ -2,8 +2,8 @@ import { ConnectorMetadata } from '@logto/schemas';
|
|||
import classNames from 'classnames';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import MoreButton from '@/components/Button/MoreButton';
|
||||
import SocialIconButton from '@/components/Button/SocialIconButton';
|
||||
import MoreSocialIcon from '@/components/Icons/MoreSocialIcon';
|
||||
import useSocial from '@/hooks/use-social';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -38,7 +38,9 @@ const SecondarySocialSignIn = ({ className, connectors, showMoreConnectors }: Pr
|
|||
}}
|
||||
/>
|
||||
))}
|
||||
{isOverSize && <MoreButton className={styles.socialButton} onClick={showMoreConnectors} />}
|
||||
{isOverSize && (
|
||||
<MoreSocialIcon className={styles.socialButton} onClick={showMoreConnectors} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,23 +3,28 @@
|
|||
.socialIconList {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
@include _.flex-row;
|
||||
justify-content: center;
|
||||
|
||||
.socialButton {
|
||||
margin-right: _.unit(10);
|
||||
margin-right: _.unit(8);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.moreButton {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.socialLinkList {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
@include _.flex-column;
|
||||
margin: 0 auto;
|
||||
@include _.flex-column;
|
||||
|
||||
.socialLinkButton {
|
||||
margin-bottom: _.unit(4);
|
||||
|
|
|
@ -3,34 +3,18 @@
|
|||
.form {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
@include _.flex-column;
|
||||
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputField:first-child {
|
||||
.inputField {
|
||||
margin-bottom: _.unit(4);
|
||||
|
||||
&.withError {
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
||||
.textLink {
|
||||
margin-top: _.unit(3);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.inputField.withError + .textLink {
|
||||
margin-top: _.unit(1);
|
||||
}
|
||||
|
||||
.terms {
|
||||
margin: _.unit(6) 0;
|
||||
|
||||
&.withError {
|
||||
margin: _.unit(5) 0 _.unit(2) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ describe('<UsernameSignin>', () => {
|
|||
expect(container.querySelector('input[name="username"]')).not.toBeNull();
|
||||
expect(container.querySelector('input[name="password"]')).not.toBeNull();
|
||||
expect(queryByText('action.sign_in')).not.toBeNull();
|
||||
expect(queryByText('description.forgot_password')).not.toBeNull();
|
||||
expect(queryByText('description.agree_with_terms')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 3. Forgot password URL
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { FC, useState, useCallback, useEffect, useContext, useMemo } from 'react';
|
||||
import React, { useState, useCallback, useEffect, useContext, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { signInBasic } from '@/apis/sign-in';
|
||||
|
@ -16,7 +15,6 @@ import { ErrorType } from '@/components/ErrorMessage';
|
|||
import Input from '@/components/Input';
|
||||
import PasswordInput from '@/components/Input/PasswordInput';
|
||||
import TermsOfUse from '@/components/TermsOfUse';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import PageContext from '@/hooks/page-context';
|
||||
import useApi from '@/hooks/use-api';
|
||||
|
||||
|
@ -36,13 +34,17 @@ type FieldValidations = {
|
|||
[key in keyof FieldState]?: (state: FieldState) => ErrorType | undefined;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const defaultState: FieldState = {
|
||||
username: '',
|
||||
password: '',
|
||||
termsAgreement: false,
|
||||
};
|
||||
|
||||
const UsernameSignin: FC = () => {
|
||||
const UsernameSignin = ({ className }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
|
@ -130,9 +132,9 @@ const UsernameSignin: FC = () => {
|
|||
}, [error, t, setToast]);
|
||||
|
||||
return (
|
||||
<form className={styles.form}>
|
||||
<form className={classNames(styles.form, className)}>
|
||||
<Input
|
||||
className={classNames(styles.inputField, fieldErrors.username && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="username"
|
||||
autoComplete="username"
|
||||
placeholder={t('input.username')}
|
||||
|
@ -149,7 +151,7 @@ const UsernameSignin: FC = () => {
|
|||
}}
|
||||
/>
|
||||
<PasswordInput
|
||||
className={classNames(styles.inputField, fieldErrors.password && styles.withError)}
|
||||
className={styles.inputField}
|
||||
name="password"
|
||||
autoComplete="current-password"
|
||||
placeholder={t('input.password')}
|
||||
|
@ -162,16 +164,10 @@ const UsernameSignin: FC = () => {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
<TextLink
|
||||
className={styles.textLink}
|
||||
type="secondary"
|
||||
text="description.forgot_password"
|
||||
href="/passcode"
|
||||
/>
|
||||
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={classNames(styles.terms, fieldErrors.termsAgreement && styles.withError)}
|
||||
className={styles.terms}
|
||||
termsOfUse={{ enabled: true, contentUrl: '/' }}
|
||||
isChecked={fieldState.termsAgreement}
|
||||
error={fieldErrors.termsAgreement}
|
||||
|
|
|
@ -6,15 +6,6 @@
|
|||
@include _.flex-column;
|
||||
}
|
||||
|
||||
.navBar {
|
||||
width: 100%;
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
svg {
|
||||
margin-left: _.unit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
@include _.title;
|
||||
|
@ -23,7 +14,7 @@
|
|||
|
||||
.detail {
|
||||
width: 100%;
|
||||
margin-bottom: _.unit(9);
|
||||
margin-bottom: _.unit(6);
|
||||
font: var(--font-body);
|
||||
color: var(--color-font-tertiary);
|
||||
color: var(--color-caption);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useParams, useLocation } from 'react-router-dom';
|
||||
|
||||
import { NavArrowIcon } from '@/components/Icons';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import PasscodeValidation from '@/containers/PasscodeValidation';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
|
@ -46,13 +46,7 @@ const Passcode = () => {
|
|||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.navBar}>
|
||||
<NavArrowIcon
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<NavBar />
|
||||
<div className={styles.title}>{t('action.enter_passcode')}</div>
|
||||
<div className={styles.detail}>{t('description.enter_passcode', { address: target })}</div>
|
||||
<PasscodeValidation type={type} method={method} target={target} />
|
||||
|
|
|
@ -6,17 +6,8 @@
|
|||
@include _.flex-column;
|
||||
}
|
||||
|
||||
.navBar {
|
||||
width: 100%;
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
svg {
|
||||
margin-left: _.unit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
@include _.title;
|
||||
margin-bottom: _.unit(9);
|
||||
margin-bottom: _.unit(6);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useMemo, useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { NavArrowIcon } from '@/components/Icons';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import CreateAccount from '@/containers/CreateAccount';
|
||||
import { PhonePasswordless, EmailPasswordless } from '@/containers/Passwordless';
|
||||
|
||||
|
@ -37,13 +37,7 @@ const Register = () => {
|
|||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.navBar}>
|
||||
<NavArrowIcon
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<NavBar />
|
||||
<div className={styles.title}>{t('action.create_account')}</div>
|
||||
{registerForm}
|
||||
</div>
|
||||
|
|
|
@ -6,17 +6,9 @@
|
|||
@include _.flex-column;
|
||||
}
|
||||
|
||||
.navBar {
|
||||
width: 100%;
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
svg {
|
||||
margin-left: _.unit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
@include _.title;
|
||||
margin-bottom: _.unit(9);
|
||||
margin-bottom: _.unit(6);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useMemo, useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { NavArrowIcon } from '@/components/Icons';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import { PhonePasswordless, EmailPasswordless } from '@/containers/Passwordless';
|
||||
import UsernameSignin from '@/containers/UsernameSignin';
|
||||
|
||||
|
@ -37,13 +37,7 @@ const SecondarySignIn = () => {
|
|||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.navBar}>
|
||||
<NavArrowIcon
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<NavBar />
|
||||
<div className={styles.title}>{t('action.sign_in')}</div>
|
||||
{signInForm}
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@include _.flex-column;
|
||||
|
||||
.header {
|
||||
margin-bottom: _.unit(10);
|
||||
margin-bottom: _.unit(12);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,12 @@ const SignIn = () => {
|
|||
logo="https://avatars.githubusercontent.com/u/84981374?s=400&u=6c44c3642f2fe15a59a56cdcb0358c0bd8b92f57&v=4"
|
||||
/>
|
||||
<UsernameSignin />
|
||||
<TextLink className={styles.createAccount} href="/register" text="action.create_account" />
|
||||
<TextLink
|
||||
className={styles.createAccount}
|
||||
type="secondary"
|
||||
href="/register"
|
||||
text="action.create_account"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
}
|
||||
|
||||
@mixin text-hint {
|
||||
font: var(--font-body-small);
|
||||
color: var(--color-font-link-secondary);
|
||||
font: var(--font-caption);
|
||||
color: var(--color-caption);
|
||||
}
|
||||
|
||||
@mixin title {
|
||||
font: var(--font-title);
|
||||
color: var(--color-font-primary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
@function border($color: transparent, $width: 1) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.modal {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
right: 40px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
outline: none;
|
||||
|
@ -19,7 +19,7 @@
|
|||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
background: rgba(0, 0, 0, 16%);
|
||||
background: var(--color-overlay);
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue