mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(ui): init destop styling foundation (#787)
* feat(ui): init destop styling foundation init desktop styling * fix(ui): cr fix * style(ui): enable mobile and desktop global class enable mobile and desktop global class
This commit is contained in:
parent
2b3061d06d
commit
5c02ec3bda
23 changed files with 239 additions and 95 deletions
|
@ -64,10 +64,21 @@
|
|||
"extends": "@silverhand/react"
|
||||
},
|
||||
"stylelint": {
|
||||
"extends": "@silverhand/eslint-config-react/.stylelintrc"
|
||||
"extends": "@silverhand/eslint-config-react/.stylelintrc",
|
||||
"rules": {
|
||||
"selector-pseudo-class-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignorePseudoClasses": [
|
||||
"global"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"prettier": "@silverhand/eslint-config/.prettierrc",
|
||||
"dependencies": {
|
||||
"react-device-detect": "^2.2.2",
|
||||
"use-debounced-loader": "^0.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,61 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
@use '@/scss/colors' as colors;
|
||||
@use '@/scss/basic' as basic;
|
||||
@use '@/scss/mobile' as mobile;
|
||||
@use '@/scss/desktop' as desktop;
|
||||
|
||||
/* Foundation */
|
||||
.light {
|
||||
@include colors.light-theme;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@include colors.dark-theme;
|
||||
}
|
||||
|
||||
body {
|
||||
@include colors.universal;
|
||||
@include basic.fonts;
|
||||
@include basic.layout;
|
||||
background: var(--color-base);
|
||||
color: var(--color-text);
|
||||
font: var(--font-body);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 100%;
|
||||
background: var(--color-base);
|
||||
@include _.flex_column;
|
||||
background-color: var(--color-base);
|
||||
}
|
||||
|
||||
main {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
font: var(--font-body);
|
||||
@include _.flex_column;
|
||||
}
|
||||
|
||||
/* Foundation */
|
||||
:global(body.mobile) {
|
||||
&.light {
|
||||
@include mobile.colors-light-theme;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
@include mobile.colors-dark-theme;
|
||||
}
|
||||
|
||||
@include mobile.colors-universal;
|
||||
@include mobile.fonts;
|
||||
@include mobile.layout;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
padding: _.unit(6) _.unit(5);
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
:global(body.desktop) {
|
||||
&.light {
|
||||
@include desktop.colors-light-theme;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
@include desktop.colors-dark-theme;
|
||||
}
|
||||
|
||||
@include desktop.colors-universal;
|
||||
@include desktop.fonts;
|
||||
@include desktop.layout;
|
||||
|
||||
.content {
|
||||
width: 640px;
|
||||
min-height: 640px;
|
||||
position: relative;
|
||||
padding: _.unit(6);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import { conditionalString } from '@silverhand/essentials';
|
||||
import React, { ReactNode, useEffect, useCallback, useContext } from 'react';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
import { useDebouncedLoader } from 'use-debounced-loader';
|
||||
|
||||
import LoadingLayer from '@/components/LoadingLayer';
|
||||
|
@ -24,14 +25,17 @@ const AppContent = ({ children }: Props) => {
|
|||
}, [setToast]);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.remove(styles.light ?? '');
|
||||
document.body.classList.remove(styles.dark ?? '');
|
||||
document.body.classList.add(styles[theme] ?? '');
|
||||
document.body.classList.remove(conditionalString(styles.light), conditionalString(styles.dark));
|
||||
document.body.classList.add(conditionalString(styles[theme]));
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add(isMobile ? 'mobile' : 'desktop');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className={classNames(styles.content, styles[theme])}>
|
||||
{children}
|
||||
<main>
|
||||
<div className={styles.content}>{children}</div>
|
||||
<Toast message={toast} isVisible={Boolean(toast)} callback={hideToast} />
|
||||
{debouncedLoading && <LoadingLayer />}
|
||||
</main>
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
/* stylelint-disable-next-line selector-pseudo-class-no-unknown */
|
||||
:global {
|
||||
.ReactModal__Content[role='popup'] {
|
||||
transform: translateY(100%);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
svg {
|
||||
position: absolute;
|
||||
left: _.unit(4);
|
||||
left: _.unit(-2);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
fill: var(--color-text);
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@include _.full-page;
|
||||
@include _.flex-column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.connector {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
@include _.full-page;
|
||||
@include _.flex-column;
|
||||
justify-content: flex-start;
|
||||
|
||||
.header {
|
||||
margin-top: _.unit(30);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
.wrapper {
|
||||
@include _.full-page;
|
||||
@include _.flex-column;
|
||||
align-items: stretch;
|
||||
|
||||
.wrapper {
|
||||
.container {
|
||||
flex: 1;
|
||||
@include _.page-wrapper;
|
||||
@include _.flex-column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
|
|
@ -18,9 +18,9 @@ const ErrorPage = ({ title = 'description.not_found', message }: Props) => {
|
|||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.wrapper}>
|
||||
<NavBar />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.container}>
|
||||
<ErrorIcon className={styles.icon} />
|
||||
<div className={styles.title}>{t(title)}</div>
|
||||
{message && <div className={styles.message}>{message}</div>}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
padding-top: _.unit(2);
|
||||
@include _.full-page;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
@ -43,14 +43,14 @@ const Passcode = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.wrapper}>
|
||||
<NavBar />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.container}>
|
||||
<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} />
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
padding-top: _.unit(2);
|
||||
@include _.full-page;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -34,13 +34,13 @@ const Register = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.wrapper}>
|
||||
<NavBar />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.title}>{t('action.create_account')}</div>
|
||||
{registerForm}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
padding-top: _.unit(2);
|
||||
@include _.full-page;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -34,13 +34,13 @@ const SecondarySignIn = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.wrapper}>
|
||||
<NavBar />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.title}>{t('action.sign_in')}</div>
|
||||
{signInForm}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
@include _.full-page;
|
||||
@include _.flex-column;
|
||||
justify-content: flex-start;
|
||||
|
||||
.header {
|
||||
margin: _.unit(8);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.wrapper {
|
||||
@include _.page-wrapper;
|
||||
@include _.full-page;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: _.unit(2);
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ const SocialRegister = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.wrapper}>
|
||||
<NavBar title={t('description.bind_account_title')} />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.container}>
|
||||
<SocialCreateAccount connectorId={connector} />
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/* Foundation */
|
||||
$font-family: -apple-system,
|
||||
system-ui,
|
||||
'BlinkMacSystemFont',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Helvetica Neue',
|
||||
'Helvetica',
|
||||
'Arial',
|
||||
sans-serif;
|
||||
|
||||
@mixin fonts {
|
||||
--font-title: 600 32px/40px #{$font-family};
|
||||
--font-body-bold: 500 16px/20px #{$font-family};
|
||||
--font-body: 400 16px/20px #{$font-family};
|
||||
--font-body-small: 500 14px/18px #{$font-family};
|
||||
--font-caption: 400 14px/18px #{$font-family};
|
||||
}
|
||||
|
||||
@mixin layout {
|
||||
--radius: 8px;
|
||||
}
|
63
packages/ui/src/scss/_desktop.scss
Normal file
63
packages/ui/src/scss/_desktop.scss
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* Foundation */
|
||||
$font-family: -apple-system,
|
||||
system-ui,
|
||||
'BlinkMacSystemFont',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Helvetica Neue',
|
||||
'Helvetica',
|
||||
'Arial',
|
||||
sans-serif;
|
||||
|
||||
@mixin colors-light-theme {
|
||||
--color-surface: #ecebf6;
|
||||
--color-base: #fff;
|
||||
// legacy bellow
|
||||
--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-dialogue: #fff;
|
||||
}
|
||||
|
||||
@mixin colors-dark-theme {
|
||||
--color-surface: #25272b;
|
||||
--color-base: #2a2c32;
|
||||
// legacy bellow
|
||||
--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(25, 28, 29, 40%);
|
||||
--color-dialogue: linear-gradient(0deg, rgba(202, 190, 255, 8%), rgba(202, 190, 255, 8%)), linear-gradient(0deg, rgba(196, 199, 199, 2%), rgba(196, 199, 199, 2%)), #191c1d;
|
||||
}
|
||||
|
||||
|
||||
@mixin colors-universal {
|
||||
--color-primary-button-text: #fff;
|
||||
}
|
||||
|
||||
@mixin fonts {
|
||||
--font-title: 600 32px/40px #{$font-family};
|
||||
--font-body-bold: 500 16px/20px #{$font-family};
|
||||
--font-body: 400 16px/20px #{$font-family};
|
||||
--font-body-small: 500 14px/18px #{$font-family};
|
||||
--font-caption: 400 14px/18px #{$font-family};
|
||||
}
|
||||
|
||||
@mixin layout {
|
||||
--radius: 8px;
|
||||
}
|
|
@ -1,4 +1,15 @@
|
|||
@mixin light-theme {
|
||||
/* Foundation */
|
||||
$font-family: -apple-system,
|
||||
system-ui,
|
||||
'BlinkMacSystemFont',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Helvetica Neue',
|
||||
'Helvetica',
|
||||
'Arial',
|
||||
sans-serif;
|
||||
|
||||
@mixin colors-light-theme {
|
||||
--color-text: #191c1d;
|
||||
--color-icon: #747778;
|
||||
--color-caption: #747778;
|
||||
|
@ -11,10 +22,11 @@
|
|||
--color-toast: rgba(25, 28, 29, 80%);
|
||||
--color-overlay: rgba(25, 28, 29, 16%);
|
||||
--color-base: #fff;
|
||||
--color-surface: #fff;
|
||||
--color-dialogue: #fff;
|
||||
}
|
||||
|
||||
@mixin dark-theme {
|
||||
@mixin colors-dark-theme {
|
||||
--color-text: #f7f8f8;
|
||||
--color-icon: #a9acac;
|
||||
--color-caption: #a9acac;
|
||||
|
@ -27,9 +39,23 @@
|
|||
--color-toast: rgba(247, 248, 248, 80%);
|
||||
--color-overlay: rgba(25, 28, 29, 40%);
|
||||
--color-base: #191c1d;
|
||||
--color-surface: #191c1d;
|
||||
--color-dialogue: linear-gradient(0deg, rgba(202, 190, 255, 8%), rgba(202, 190, 255, 8%)), linear-gradient(0deg, rgba(196, 199, 199, 2%), rgba(196, 199, 199, 2%)), #191c1d;
|
||||
}
|
||||
|
||||
@mixin universal {
|
||||
|
||||
@mixin colors-universal {
|
||||
--color-primary-button-text: #fff;
|
||||
}
|
||||
|
||||
@mixin fonts {
|
||||
--font-title: 600 32px/40px #{$font-family};
|
||||
--font-body-bold: 500 16px/20px #{$font-family};
|
||||
--font-body: 400 16px/20px #{$font-family};
|
||||
--font-body-small: 500 14px/18px #{$font-family};
|
||||
--font-caption: 400 14px/18px #{$font-family};
|
||||
}
|
||||
|
||||
@mixin layout {
|
||||
--radius: 8px;
|
||||
}
|
|
@ -29,11 +29,9 @@
|
|||
color: var(--color-text);
|
||||
}
|
||||
|
||||
@mixin page-wrapper {
|
||||
position: relative;
|
||||
padding: unit(6) unit(5);
|
||||
@include flex-column;
|
||||
justify-content: flex-start;
|
||||
@mixin full-page {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
@mixin container-width {
|
||||
|
|
|
@ -864,6 +864,7 @@ importers:
|
|||
postcss-modules: ^4.3.0
|
||||
prettier: ^2.3.2
|
||||
react: ^17.0.2
|
||||
react-device-detect: ^2.2.2
|
||||
react-dom: ^17.0.2
|
||||
react-i18next: ^11.15.4
|
||||
react-modal: ^3.14.4
|
||||
|
@ -875,6 +876,7 @@ importers:
|
|||
typescript: ^4.6.2
|
||||
use-debounced-loader: ^0.1.1
|
||||
dependencies:
|
||||
react-device-detect: 2.2.2_sfoxds7t5ydpegc3knd667wn6m
|
||||
use-debounced-loader: 0.1.1_react@17.0.2
|
||||
devDependencies:
|
||||
'@logto/jest-config': link:../jest-config
|
||||
|
@ -16755,6 +16757,17 @@ packages:
|
|||
- webpack
|
||||
dev: true
|
||||
|
||||
/react-device-detect/2.2.2_sfoxds7t5ydpegc3knd667wn6m:
|
||||
resolution: {integrity: sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==}
|
||||
peerDependencies:
|
||||
react: '>= 0.14.0'
|
||||
react-dom: '>= 0.14.0'
|
||||
dependencies:
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
ua-parser-js: 1.0.2
|
||||
dev: false
|
||||
|
||||
/react-dnd-html5-backend/16.0.0:
|
||||
resolution: {integrity: sha512-be3lKEbbT8FQcoTjFlQ4ZXG/NVrFNJu9W0INc5rm/5EFQpHCkz+xpbB2U8j9uh5Bvk7AsJyQrZznEut0hrNPIA==}
|
||||
dependencies:
|
||||
|
@ -19530,6 +19543,10 @@ packages:
|
|||
resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==}
|
||||
dev: true
|
||||
|
||||
/ua-parser-js/1.0.2:
|
||||
resolution: {integrity: sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==}
|
||||
dev: false
|
||||
|
||||
/uglify-js/3.15.3:
|
||||
resolution: {integrity: sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
|
Loading…
Reference in a new issue