0
Fork 0
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:
simeng-li 2022-05-11 11:30:35 +08:00 committed by GitHub
parent 2b3061d06d
commit 5c02ec3bda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 239 additions and 95 deletions

View file

@ -64,10 +64,21 @@
"extends": "@silverhand/react" "extends": "@silverhand/react"
}, },
"stylelint": { "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", "prettier": "@silverhand/eslint-config/.prettierrc",
"dependencies": { "dependencies": {
"react-device-detect": "^2.2.2",
"use-debounced-loader": "^0.1.1" "use-debounced-loader": "^0.1.1"
} }
} }

View file

@ -1,30 +1,61 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
@use '@/scss/colors' as colors; @use '@/scss/mobile' as mobile;
@use '@/scss/basic' as basic; @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 { .content {
position: absolute; @include _.flex_column;
top: 0; background-color: var(--color-base);
left: 0; }
right: 0;
min-height: 100%; main {
background: var(--color-base); 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);
}
} }

View file

@ -1,5 +1,6 @@
import classNames from 'classnames'; import { conditionalString } from '@silverhand/essentials';
import React, { ReactNode, useEffect, useCallback, useContext } from 'react'; import React, { ReactNode, useEffect, useCallback, useContext } from 'react';
import { isMobile } from 'react-device-detect';
import { useDebouncedLoader } from 'use-debounced-loader'; import { useDebouncedLoader } from 'use-debounced-loader';
import LoadingLayer from '@/components/LoadingLayer'; import LoadingLayer from '@/components/LoadingLayer';
@ -24,14 +25,17 @@ const AppContent = ({ children }: Props) => {
}, [setToast]); }, [setToast]);
useEffect(() => { useEffect(() => {
document.body.classList.remove(styles.light ?? ''); document.body.classList.remove(conditionalString(styles.light), conditionalString(styles.dark));
document.body.classList.remove(styles.dark ?? ''); document.body.classList.add(conditionalString(styles[theme]));
document.body.classList.add(styles[theme] ?? '');
}, [theme]); }, [theme]);
useEffect(() => {
document.body.classList.add(isMobile ? 'mobile' : 'desktop');
}, []);
return ( return (
<main className={classNames(styles.content, styles[theme])}> <main>
{children} <div className={styles.content}>{children}</div>
<Toast message={toast} isVisible={Boolean(toast)} callback={hideToast} /> <Toast message={toast} isVisible={Boolean(toast)} callback={hideToast} />
{debouncedLoading && <LoadingLayer />} {debouncedLoading && <LoadingLayer />}
</main> </main>

View file

@ -30,7 +30,6 @@
/* stylelint-disable selector-class-pattern */ /* stylelint-disable selector-class-pattern */
/* stylelint-disable-next-line selector-pseudo-class-no-unknown */
:global { :global {
.ReactModal__Content[role='popup'] { .ReactModal__Content[role='popup'] {
transform: translateY(100%); transform: translateY(100%);

View file

@ -10,7 +10,7 @@
svg { svg {
position: absolute; position: absolute;
left: _.unit(4); left: _.unit(-2);
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
fill: var(--color-text); fill: var(--color-text);

View file

@ -2,9 +2,9 @@
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
position: absolute; @include _.flex-column;
inset: 0; justify-content: flex-start;
} }
.connector { .connector {

View file

@ -1,7 +1,9 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
@include _.flex-column;
justify-content: flex-start;
.header { .header {
margin-top: _.unit(30); margin-top: _.unit(30);

View file

@ -1,14 +1,14 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.container { .wrapper {
position: absolute; @include _.full-page;
inset: 0;
@include _.flex-column; @include _.flex-column;
align-items: stretch; align-items: stretch;
.wrapper { .container {
flex: 1; flex: 1;
@include _.page-wrapper; @include _.flex-column;
justify-content: flex-start;
} }
.icon { .icon {

View file

@ -18,9 +18,9 @@ const ErrorPage = ({ title = 'description.not_found', message }: Props) => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className={styles.container}> <div className={styles.wrapper}>
<NavBar /> <NavBar />
<div className={styles.wrapper}> <div className={styles.container}>
<ErrorIcon className={styles.icon} /> <ErrorIcon className={styles.icon} />
<div className={styles.title}>{t(title)}</div> <div className={styles.title}>{t(title)}</div>
{message && <div className={styles.message}>{message}</div>} {message && <div className={styles.message}>{message}</div>}

View file

@ -1,8 +1,11 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
padding-top: _.unit(2); }
.container {
margin-top: _.unit(2);
} }
.title { .title {

View file

@ -43,14 +43,14 @@ const Passcode = () => {
} }
return ( return (
<> <div className={styles.wrapper}>
<NavBar /> <NavBar />
<div className={styles.wrapper}> <div className={styles.container}>
<div className={styles.title}>{t('action.enter_passcode')}</div> <div className={styles.title}>{t('action.enter_passcode')}</div>
<div className={styles.detail}>{t('description.enter_passcode', { address: target })}</div> <div className={styles.detail}>{t('description.enter_passcode', { address: target })}</div>
<PasscodeValidation type={type} method={method} target={target} /> <PasscodeValidation type={type} method={method} target={target} />
</div> </div>
</> </div>
); );
}; };

View file

@ -1,8 +1,11 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
padding-top: _.unit(2); }
.container {
margin-top: _.unit(2);
} }

View file

@ -34,13 +34,13 @@ const Register = () => {
} }
return ( return (
<> <div className={styles.wrapper}>
<NavBar /> <NavBar />
<div className={styles.wrapper}> <div className={styles.container}>
<div className={styles.title}>{t('action.create_account')}</div> <div className={styles.title}>{t('action.create_account')}</div>
{registerForm} {registerForm}
</div> </div>
</> </div>
); );
}; };

View file

@ -1,8 +1,11 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
padding-top: _.unit(2); }
.container {
margin-top: _.unit(2);
} }

View file

@ -34,13 +34,13 @@ const SecondarySignIn = () => {
} }
return ( return (
<> <div className={styles.wrapper}>
<NavBar /> <NavBar />
<div className={styles.wrapper}> <div className={styles.container}>
<div className={styles.title}>{t('action.sign_in')}</div> <div className={styles.title}>{t('action.sign_in')}</div>
{signInForm} {signInForm}
</div> </div>
</> </div>
); );
}; };

View file

@ -1,7 +1,9 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
@include _.flex-column;
justify-content: flex-start;
.header { .header {
margin: _.unit(8); margin: _.unit(8);

View file

@ -1,5 +1,9 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.wrapper { .wrapper {
@include _.page-wrapper; @include _.full-page;
}
.container {
margin-top: _.unit(2);
} }

View file

@ -20,12 +20,12 @@ const SocialRegister = () => {
} }
return ( return (
<> <div className={styles.wrapper}>
<NavBar title={t('description.bind_account_title')} /> <NavBar title={t('description.bind_account_title')} />
<div className={styles.wrapper}> <div className={styles.container}>
<SocialCreateAccount connectorId={connector} /> <SocialCreateAccount connectorId={connector} />
</div> </div>
</> </div>
); );
}; };

View file

@ -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;
}

View 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;
}

View file

@ -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-text: #191c1d;
--color-icon: #747778; --color-icon: #747778;
--color-caption: #747778; --color-caption: #747778;
@ -11,10 +22,11 @@
--color-toast: rgba(25, 28, 29, 80%); --color-toast: rgba(25, 28, 29, 80%);
--color-overlay: rgba(25, 28, 29, 16%); --color-overlay: rgba(25, 28, 29, 16%);
--color-base: #fff; --color-base: #fff;
--color-surface: #fff;
--color-dialogue: #fff; --color-dialogue: #fff;
} }
@mixin dark-theme { @mixin colors-dark-theme {
--color-text: #f7f8f8; --color-text: #f7f8f8;
--color-icon: #a9acac; --color-icon: #a9acac;
--color-caption: #a9acac; --color-caption: #a9acac;
@ -27,9 +39,23 @@
--color-toast: rgba(247, 248, 248, 80%); --color-toast: rgba(247, 248, 248, 80%);
--color-overlay: rgba(25, 28, 29, 40%); --color-overlay: rgba(25, 28, 29, 40%);
--color-base: #191c1d; --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; --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; --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;
}

View file

@ -29,11 +29,9 @@
color: var(--color-text); color: var(--color-text);
} }
@mixin page-wrapper { @mixin full-page {
position: relative; flex: 1;
padding: unit(6) unit(5); align-self: stretch;
@include flex-column;
justify-content: flex-start;
} }
@mixin container-width { @mixin container-width {

View file

@ -864,6 +864,7 @@ importers:
postcss-modules: ^4.3.0 postcss-modules: ^4.3.0
prettier: ^2.3.2 prettier: ^2.3.2
react: ^17.0.2 react: ^17.0.2
react-device-detect: ^2.2.2
react-dom: ^17.0.2 react-dom: ^17.0.2
react-i18next: ^11.15.4 react-i18next: ^11.15.4
react-modal: ^3.14.4 react-modal: ^3.14.4
@ -875,6 +876,7 @@ importers:
typescript: ^4.6.2 typescript: ^4.6.2
use-debounced-loader: ^0.1.1 use-debounced-loader: ^0.1.1
dependencies: dependencies:
react-device-detect: 2.2.2_sfoxds7t5ydpegc3knd667wn6m
use-debounced-loader: 0.1.1_react@17.0.2 use-debounced-loader: 0.1.1_react@17.0.2
devDependencies: devDependencies:
'@logto/jest-config': link:../jest-config '@logto/jest-config': link:../jest-config
@ -16755,6 +16757,17 @@ packages:
- webpack - webpack
dev: true 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: /react-dnd-html5-backend/16.0.0:
resolution: {integrity: sha512-be3lKEbbT8FQcoTjFlQ4ZXG/NVrFNJu9W0INc5rm/5EFQpHCkz+xpbB2U8j9uh5Bvk7AsJyQrZznEut0hrNPIA==} resolution: {integrity: sha512-be3lKEbbT8FQcoTjFlQ4ZXG/NVrFNJu9W0INc5rm/5EFQpHCkz+xpbB2U8j9uh5Bvk7AsJyQrZznEut0hrNPIA==}
dependencies: dependencies:
@ -19530,6 +19543,10 @@ packages:
resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==} resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==}
dev: true 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: /uglify-js/3.15.3:
resolution: {integrity: sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==} resolution: {integrity: sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}