0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

style(ui): implement dynamic color theme (#938)

* style(ui): implement dynamic color theme

implement dynamic color theme

* fix(ui): avoid precision issue

avoid precision issue

* fix(ui): fix typo

* fix(ui): fix typo

fix typo
This commit is contained in:
simeng-li 2022-05-25 09:58:54 +08:00 committed by GitHub
parent 7a17d41acf
commit 48607219f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 34 deletions

View file

@ -29,12 +29,14 @@
"@silverhand/ts-config": "^0.14.0",
"@silverhand/ts-config-react": "^0.14.0",
"@testing-library/react": "^12.0.0",
"@types/color": "^3.0.3",
"@types/jest": "^27.4.1",
"@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9",
"@types/react-modal": "^3.13.1",
"@types/react-router-dom": "^5.3.2",
"classnames": "^2.3.1",
"color": "^4.2.3",
"cross-env": "^7.0.3",
"eslint": "^8.10.0",
"i18next": "^21.6.12",

View file

@ -4,7 +4,17 @@
:root {
--light-primary-color: #5d34f2;
--light-focused-variant: rgba(93, 52, 242, 16%);
--light-hover-variant: rgba(93, 52, 242, 8%);
--light-pressed-variant: rgba(93, 52, 242, 12%);
--light-hover: #7350f4;
--light-pressed: #4718f0;
--dark-primary-color: #7958ff;
--dark-focused-variant: rgba(121, 88, 255, 16%);
--dark-hover-variant: rgba(121, 88, 255, 8%);
--dark-pressed-variant: rgba(121, 88, 255, 12%);
--dark-hover: #957aff;
--dark-pressed: #5d36ff;
}
.content {

View file

@ -4,6 +4,7 @@ import { useDebouncedLoader } from 'use-debounced-loader';
import LoadingLayer from '@/components/LoadingLayer';
import Toast from '@/components/Toast';
import useColorTheme from '@/hooks/use-color-theme';
import { PageContext } from '@/hooks/use-page-context';
import useTheme from '@/hooks/use-theme';
@ -23,22 +24,11 @@ const AppContent = ({ children }: Props) => {
setToast('');
}, [setToast]);
// Set Primary ColorTheme
useEffect(() => {
if (!experienceSettings) {
return;
}
const {
branding: { primaryColor, darkPrimaryColor },
} = experienceSettings;
document.documentElement.style.setProperty('--light-primary-color', primaryColor);
document.documentElement.style.setProperty(
'--dark-primary-color',
darkPrimaryColor ?? primaryColor
);
}, [experienceSettings]);
// Set Primary Color
useColorTheme(
experienceSettings?.branding.primaryColor,
experienceSettings?.branding.darkPrimaryColor
);
// Set Theme Mode
useEffect(() => {

View file

@ -0,0 +1,59 @@
import color from 'color';
import { useEffect } from 'react';
// Color hsl lighten/darken takes percentage value only, need to implement absolute value update
const absoluteLighten = (baseColor: color, delta: number) => {
const hslArray = baseColor.hsl().round().array() as [number, number, number];
return color([hslArray[0], hslArray[1], hslArray[2] + delta], 'hsl');
};
const absoluteDarken = (baseColor: color, delta: number) => {
const hslArray = baseColor.hsl().round().array() as [number, number, number];
return color([hslArray[0], hslArray[1], hslArray[2] - delta], 'hsl');
};
const generateLightColorLibrary = (primaryColor: color) => ({
[`--light-primary-color`]: primaryColor.hex(),
[`--light-focused-variant`]: primaryColor.alpha(0.16).string(),
[`--light-hover-variant`]: primaryColor.alpha(0.08).string(),
[`--light-pressed-variant`]: primaryColor.alpha(0.12).string(),
[`--light-hover`]: absoluteLighten(primaryColor, 10).string(),
[`--light-pressed`]: absoluteDarken(primaryColor, 10).string(),
});
const generateDarkColorLibrary = (primaryColor: color) => ({
[`--dark-primary-color`]: primaryColor.hex(),
[`--dark-focused-variant`]: absoluteLighten(primaryColor, 17).rgb().alpha(0.16).string(),
[`--dark-hover-variant`]: absoluteLighten(primaryColor, 17).rgb().alpha(0.08).string(),
[`--dark-pressed-variant`]: absoluteLighten(primaryColor, 17).rgb().alpha(0.12).string(),
[`--dark-hover`]: absoluteLighten(primaryColor, 10).string(),
[`--dark-pressed`]: absoluteDarken(primaryColor, 10).string(),
});
const useColorTheme = (primaryColor?: string, darkPrimaryColor?: string) => {
useEffect(() => {
if (!primaryColor) {
return;
}
const lightPrimary = color(primaryColor);
const darkPrimary = darkPrimaryColor
? color(darkPrimaryColor)
: absoluteLighten(lightPrimary, 10);
const lightColorLibrary = generateLightColorLibrary(lightPrimary);
const darkColorLibrary = generateDarkColorLibrary(darkPrimary);
for (const [key, value] of Object.entries(lightColorLibrary)) {
document.documentElement.style.setProperty(key, value);
}
for (const [key, value] of Object.entries(darkColorLibrary)) {
document.documentElement.style.setProperty(key, value);
}
}, [darkPrimaryColor, primaryColor]);
};
export default useColorTheme;

View file

@ -12,20 +12,22 @@ $font-family: -apple-system,
@mixin colors-light-theme {
--color-base: linear-gradient(0deg, rgba(93, 52, 242, 14%), rgba(93, 52, 242, 14%)), linear-gradient(0deg, rgba(120, 118, 127, 2%), rgba(120, 118, 127, 2%)), #f7f8f8;
--color-surface: #fff;
--color-text: #191c1d;
--color-text: #191c1d; // Neutral-10
--color-text-disabled: #c4c7c7;
--color-border: #c4c7c7;
--color-caption: #747778;
--color-icon: #747778;
--color-focused: rgba(25, 28, 29, 16%); // 16% Neutral-10
--color-pressed: rgba(25, 28, 29, 12%); // 12% Neutral-10
--color-hover: rgba(25, 28, 29, 8%); // 8% Neutral-10
--color-primary: var(--light-primary-color);
--color-focused: rgba(25, 28, 29, 16%);
--color-focused-variant: rgba(93, 52, 242, 16%);
--color-pressed: rgba(25, 28, 29, 12%);
--color-primary-pressed: #4300da;
--color-pressed-variant: rgba(93, 52, 242, 12%);
--color-hover: rgba(25, 28, 29, 8%);
--color-primary-hover: #7958ff;
--color-hover-variant: rgba(93, 52, 242, 8%);
--color-focused-variant: var(--light-focused-variant);
--color-hover-variant: var(--light-hover-variant);
--color-pressed-variant: var(--light-pressed-variant);
--color-primary-pressed: var(--light-pressed);
--color-primary-hover: var(--light-hover);
--color-inverse-on-surface: #f3effa;
--color-outline: #78767f;
--color-layer: #eff1f1;
@ -37,20 +39,23 @@ $font-family: -apple-system,
@mixin colors-dark-theme {
--color-base: 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-surface: #191c1d;
--color-text: #f7f8f8;
--color-text: #f7f8f8; // Neutral-10
--color-text-disabled: #5c5f60;
--color-border: #5c5f60;
--color-caption: #a9acac;
--color-icon: #a9acac;
--color-focused: rgba(247, 248, 248, 16%); // 16% Neutral-10
--color-pressed: rgba(247, 248, 248, 12%); // 12% Neutral-10
--color-hover: rgba(247, 248, 248, 8%); // 8% Neutral-10
--color-primary: var(--dark-primary-color);
--color-focused: rgba(247, 248, 248, 16%);
--color-focused-variant: rgba(202, 190, 255, 16%);
--color-hover-variant: rgba(202, 190, 255, 8%);
--color-hover: rgba(247, 248, 248, 8%);
--color-primary-hover: #947dff;
--color-pressed-variant: rgba(202, 190, 255, 12%);
--color-pressed: rgba(247, 248, 248, 12%);
--color-primary-pressed: #5d34f2;
--color-focused-variant: var(--dark-focused-variant);
--color-hover-variant: var(--dark-hover-variant);
--color-pressed-variant: var(--dark-pressed-variant);
--color-primary-pressed: var(--dark-pressed);
--color-primary-hover: var(--dark-hover);
--color-inverse-on-surface: #2d3132;
--color-outline: #928f9a;
--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;

45
pnpm-lock.yaml generated
View file

@ -1002,12 +1002,14 @@ importers:
'@silverhand/ts-config': ^0.14.0
'@silverhand/ts-config-react': ^0.14.0
'@testing-library/react': ^12.0.0
'@types/color': ^3.0.3
'@types/jest': ^27.4.1
'@types/react': ^17.0.14
'@types/react-dom': ^17.0.9
'@types/react-modal': ^3.13.1
'@types/react-router-dom': ^5.3.2
classnames: ^2.3.1
color: ^4.2.3
cross-env: ^7.0.3
eslint: ^8.10.0
i18next: ^21.6.12
@ -1049,12 +1051,14 @@ importers:
'@silverhand/ts-config': 0.14.0_typescript@4.6.2
'@silverhand/ts-config-react': 0.14.0_typescript@4.6.2
'@testing-library/react': 12.1.5_sfoxds7t5ydpegc3knd667wn6m
'@types/color': 3.0.3
'@types/jest': 27.4.1
'@types/react': 17.0.37
'@types/react-dom': 17.0.11
'@types/react-modal': 3.13.1
'@types/react-router-dom': 5.3.2
classnames: 2.3.1
color: 4.2.3
cross-env: 7.0.3
eslint: 8.10.0
i18next: 21.6.12
@ -6232,6 +6236,22 @@ packages:
'@types/node': 17.0.23
'@types/responselike': 1.0.0
/@types/color-convert/2.0.0:
resolution: {integrity: sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==}
dependencies:
'@types/color-name': 1.1.1
dev: true
/@types/color-name/1.1.1:
resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==}
dev: true
/@types/color/3.0.3:
resolution: {integrity: sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==}
dependencies:
'@types/color-convert': 2.0.0
dev: true
/@types/connect-history-api-fallback/1.3.5:
resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==}
dependencies:
@ -8414,11 +8434,26 @@ packages:
/color-name/1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/color-string/1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
dev: true
/color-support/1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
dev: false
/color/4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
dev: true
/colord/2.9.2:
resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==}
dev: true
@ -11827,6 +11862,10 @@ packages:
/is-arrayish/0.2.1:
resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=}
/is-arrayish/0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
dev: true
/is-bigint/1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
@ -17747,6 +17786,12 @@ packages:
/signal-exit/3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
/simple-swizzle/0.2.2:
resolution: {integrity: sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=}
dependencies:
is-arrayish: 0.3.2
dev: true
/sirv/1.0.19:
resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==}
engines: {node: '>= 10'}