Commit d65da79e authored by N's avatar N Committed by GitHub

Merge pull request #49 from blockscout/color-mode-smooth-transition

color mode smooth transition
parents b9718900 d7f50704
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.557 7.712a.75.75 0 0 1 .442.729A7.983 7.983 0 1 1 7.526 0a.75.75 0 0 1 .548 1.308l-.18.161a4.675 4.675 0 0 0 6.619 6.603l.207-.207a.75.75 0 0 1 .837-.154Zm-1.446 2.504a6.176 6.176 0 0 1-8.373-8.311 6.481 6.481 0 0 0-2.282 10.657 6.482 6.482 0 0 0 10.655-2.346Z" fill="currentColor"/>
</svg>
\ No newline at end of file
......@@ -3,6 +3,7 @@ import type { ComponentStyleConfig } from '@chakra-ui/theme';
import type { PartsStyleFunction, SystemStyleObject } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
const sizes: Record<string, SystemStyleObject> = {
md: {
......@@ -17,13 +18,15 @@ const sizes: Record<string, SystemStyleObject> = {
const variantOutline: PartsStyleFunction<typeof parts> = (props) => {
const { theme } = props
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props)
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps();
return {
field: {
border: '2px solid',
bg: 'inherit',
borderColor: mode('gray.100', 'whiteAlpha.200')(props),
...transitionProps,
_hover: {
borderColor: mode('gray.300', 'whiteAlpha.400')(props),
},
......@@ -51,6 +54,7 @@ const variantOutline: PartsStyleFunction<typeof parts> = (props) => {
border: '2px solid',
borderColor: mode('gray.100', 'whiteAlpha.200')(props),
bg: mode('gray.100', 'whiteAlpha.200')(props),
...transitionProps,
},
}
}
......
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools';
import type { SystemStyleFunction } from '@chakra-ui/theme-tools';
import type { SystemStyleFunction, SystemStyleInterpolation } from '@chakra-ui/theme-tools';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
const baseStyle: SystemStyleInterpolation = {
...getDefaultTransitionProps(),
}
const variantPrimary: SystemStyleFunction = (props) => {
return {
......@@ -32,6 +37,7 @@ const defaultProps = {
const Link: ComponentStyleConfig = {
variants,
defaultProps,
baseStyle,
}
export default Link;
......@@ -2,18 +2,24 @@ import type { tableAnatomy as parts } from '@chakra-ui/anatomy';
import type { ComponentMultiStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools';
import type { PartsStyleFunction } from '@chakra-ui/theme-tools';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
const variantSimple: PartsStyleFunction<typeof parts> = (props) => {
const transitionProps = getDefaultTransitionProps();
return {
th: {
border: 0,
color: mode('gray.500', 'gray.50')(props),
...transitionProps,
},
thead: {
backgroundColor: mode('gray.50', 'whiteAlpha.200')(props),
...transitionProps,
},
td: {
borderColor: mode('gray.200', 'whiteAlpha.200')(props),
...transitionProps,
},
}
}
......
......@@ -2,12 +2,16 @@ import type { tagAnatomy as parts } from '@chakra-ui/anatomy';
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools';
import type { PartsStyleFunction } from '@chakra-ui/theme-tools';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
const variantGray: PartsStyleFunction<typeof parts> = (props) => {
const transitionProps = getDefaultTransitionProps();
return {
container: {
bg: mode('gray.200', 'gray.600')(props),
color: mode('gray.600', 'gray.50')(props),
...transitionProps,
},
}
}
......
......@@ -3,6 +3,7 @@ import { type ThemeConfig } from '@chakra-ui/react';
const config: ThemeConfig = {
initialColorMode: 'system',
useSystemColorMode: false,
disableTransitionOnChange: false,
}
export default config;
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
const global = (props: StyleFunctionProps) => ({
body: {
bg: mode('white', 'black')(props),
...getDefaultTransitionProps(),
},
})
......
export default function getDefaultTransitionProps() {
return {
transitionProperty: 'background-color, color, border-color',
transitionDuration: 'normal',
transitionTimingFunction: 'ease',
}
}
......@@ -12,13 +12,13 @@ import {
} from '@chakra-ui/system'
import { dataAttr, __DEV__ } from '@chakra-ui/utils'
import * as React from 'react'
import { SunIcon, MoonIcon } from '@chakra-ui/icons'
import { useColorMode, useColorModeValue } from '@chakra-ui/react';
import { SunIcon } from '@chakra-ui/icons'
import { useColorMode, useColorModeValue, Icon } from '@chakra-ui/react';
import getDefaultTransitionProps from '../../theme/utils/getDefaultTransitionProps';
import moonIcon from '../../icons/moon.svg';
import styles from './ColorModeToggler.module.css';
const TRANSITION_DURATION = 150;
export interface ColorModeTogglerProps
extends Omit<UseCheckboxProps, 'isIndeterminate'>,
Omit<HTMLChakraProps<'label'>, keyof UseCheckboxProps>,
......@@ -28,38 +28,34 @@ export interface ColorModeTogglerProps
export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((props, ref) => {
const ownProps = omitThemingProps(props);
const { toggleColorMode, colorMode } = useColorMode();
const [ isOn, setMode ] = React.useState(colorMode === 'light');
const {
state,
getInputProps,
getCheckboxProps,
getRootProps,
} = useCheckbox(ownProps);
} = useCheckbox({ ...ownProps, isChecked: colorMode === 'light' });
const trackBg = useColorModeValue('blackAlpha.100', 'whiteAlpha.200')
const thumbBg = useColorModeValue('white', 'black')
const transitionProps = getDefaultTransitionProps();
const trackStyles: SystemStyleObject = React.useMemo(() => ({
bg: trackBg,
}), [ trackBg ])
...transitionProps,
transitionDuration: '500ms',
}), [ trackBg, transitionProps ])
const thumbStyles: SystemStyleObject = React.useMemo(() => ({
bg: thumbBg,
transitionProperty: 'transform',
transitionDuration: `${ TRANSITION_DURATION }ms`,
}), [ thumbBg ])
const handleInputChange = React.useCallback(() => {
// was not able to make transition while consuming flag value from chakra's useColorMode hook
// that's why there is a local state for toggler and this fancy window.setTimeout
setMode((isOn) => !isOn);
window.setTimeout(toggleColorMode, TRANSITION_DURATION);
}, [ toggleColorMode ]);
...transitionProps,
transitionProperty: 'background-color, transform',
transitionDuration: '500ms',
}), [ thumbBg, transitionProps ])
return (
<chakra.label
{ ...getRootProps({ onChange: handleInputChange }) }
{ ...getRootProps({ onChange: toggleColorMode }) }
className={ styles.root }
>
<input className={ styles.input } { ...getInputProps({}, ref) }/>
......@@ -68,14 +64,25 @@ export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((prop
className={ styles.track }
__css={ trackStyles }
>
<MoonIcon className={ styles.nightIcon } boxSize={ 4 } color={ useColorModeValue('blue.600', 'white') }/>
<Icon
className={ styles.nightIcon }
boxSize={ 4 }
as={ moonIcon }
color={ useColorModeValue('blue.600', 'white') }
{ ...transitionProps }
/>
<chakra.div
className={ styles.thumb }
data-checked={ dataAttr(isOn) }
data-checked={ dataAttr(state.isChecked) }
data-hover={ dataAttr(state.isHovered) }
__css={ thumbStyles }
/>
<SunIcon className={ styles.dayIcon } boxSize={ 4 } color={ useColorModeValue('gray.500', 'blue.600') }/>
<SunIcon
className={ styles.dayIcon }
boxSize={ 4 }
color={ useColorModeValue('gray.500', 'blue.600') }
{ ...transitionProps }
/>
</chakra.div>
</chakra.label>
)
......
......@@ -2,6 +2,7 @@ import React from 'react';
import { VStack, Text, HStack, Icon, Link, useColorModeValue } from '@chakra-ui/react';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import ghIcon from 'icons/social/git.svg';
import twIcon from 'icons/social/tweet.svg';
import tgIcon from 'icons/social/telega.svg';
......@@ -20,12 +21,13 @@ const NavFooter = () => {
as="footer"
spacing={ 8 }
borderTop="1px solid"
borderColor={ useColorModeValue('gray.200', 'whiteAlpha.200') }
borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
paddingTop={ 8 }
w="100%"
alignItems="baseline"
color="gray.500"
fontSize="xs"
{ ...getDefaultTransitionProps() }
>
<HStack>
{ SOCIAL_LINKS.map(sl => {
......
......@@ -8,20 +8,35 @@ import NavFooter from './NavFooter'
import logoIcon from 'icons/logo.svg';
import networksIcon from 'icons/networks.svg';
import getDefaultTransitionProps from '../../theme/utils/getDefaultTransitionProps';
const Navigation = () => {
return (
<VStack
alignItems="flex-start"
spacing={ 12 }
borderRight="1px solid"
borderColor={ useColorModeValue('gray.200', 'whiteAlpha.200') }
borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
px={ 10 }
py={ 12 }
width="300px"
{ ...getDefaultTransitionProps() }
>
<HStack as="header" justifyContent="space-between" w="100%" px={ 4 } mb={ 2 } h={ 10 } alignItems="center">
<Icon as={ logoIcon } width="142px" height="26px" color={ useColorModeValue('blue.600', 'white') }/>
<Icon as={ networksIcon } width="20px" height="20px" color={ useColorModeValue('gray.500', 'white') }/>
<Icon
as={ logoIcon }
width="142px"
height="26px"
color={ useColorModeValue('blue.600', 'white') }
{ ...getDefaultTransitionProps() }
/>
<Icon
as={ networksIcon }
width="20px"
height="20px"
color={ useColorModeValue('gray.500', 'white') }
{ ...getDefaultTransitionProps() }
/>
</HStack>
<MainNavigation/>
<AccountNavigation/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment