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'; ...@@ -3,6 +3,7 @@ import type { ComponentStyleConfig } from '@chakra-ui/theme';
import type { PartsStyleFunction, SystemStyleObject } from '@chakra-ui/theme-tools'; import type { PartsStyleFunction, SystemStyleObject } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools'; import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getDefaultFormColors from '../utils/getDefaultFormColors';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
const sizes: Record<string, SystemStyleObject> = { const sizes: Record<string, SystemStyleObject> = {
md: { md: {
...@@ -17,13 +18,15 @@ const sizes: Record<string, SystemStyleObject> = { ...@@ -17,13 +18,15 @@ const sizes: Record<string, SystemStyleObject> = {
const variantOutline: PartsStyleFunction<typeof parts> = (props) => { const variantOutline: PartsStyleFunction<typeof parts> = (props) => {
const { theme } = props const { theme } = props
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props) const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps();
return { return {
field: { field: {
border: '2px solid', border: '2px solid',
bg: 'inherit', bg: 'inherit',
borderColor: mode('gray.100', 'whiteAlpha.200')(props), borderColor: mode('gray.100', 'whiteAlpha.200')(props),
...transitionProps,
_hover: { _hover: {
borderColor: mode('gray.300', 'whiteAlpha.400')(props), borderColor: mode('gray.300', 'whiteAlpha.400')(props),
}, },
...@@ -51,6 +54,7 @@ const variantOutline: PartsStyleFunction<typeof parts> = (props) => { ...@@ -51,6 +54,7 @@ const variantOutline: PartsStyleFunction<typeof parts> = (props) => {
border: '2px solid', border: '2px solid',
borderColor: mode('gray.100', 'whiteAlpha.200')(props), borderColor: mode('gray.100', 'whiteAlpha.200')(props),
bg: mode('gray.100', 'whiteAlpha.200')(props), bg: mode('gray.100', 'whiteAlpha.200')(props),
...transitionProps,
}, },
} }
} }
......
import type { ComponentStyleConfig } from '@chakra-ui/theme'; import type { ComponentStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools'; 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) => { const variantPrimary: SystemStyleFunction = (props) => {
return { return {
...@@ -32,6 +37,7 @@ const defaultProps = { ...@@ -32,6 +37,7 @@ const defaultProps = {
const Link: ComponentStyleConfig = { const Link: ComponentStyleConfig = {
variants, variants,
defaultProps, defaultProps,
baseStyle,
} }
export default Link; export default Link;
...@@ -2,18 +2,24 @@ import type { tableAnatomy as parts } from '@chakra-ui/anatomy'; ...@@ -2,18 +2,24 @@ import type { tableAnatomy as parts } from '@chakra-ui/anatomy';
import type { ComponentMultiStyleConfig } from '@chakra-ui/theme'; import type { ComponentMultiStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import type { PartsStyleFunction } 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 variantSimple: PartsStyleFunction<typeof parts> = (props) => {
const transitionProps = getDefaultTransitionProps();
return { return {
th: { th: {
border: 0, border: 0,
color: mode('gray.500', 'gray.50')(props), color: mode('gray.500', 'gray.50')(props),
...transitionProps,
}, },
thead: { thead: {
backgroundColor: mode('gray.50', 'whiteAlpha.200')(props), backgroundColor: mode('gray.50', 'whiteAlpha.200')(props),
...transitionProps,
}, },
td: { td: {
borderColor: mode('gray.200', 'whiteAlpha.200')(props), borderColor: mode('gray.200', 'whiteAlpha.200')(props),
...transitionProps,
}, },
} }
} }
......
...@@ -2,12 +2,16 @@ import type { tagAnatomy as parts } from '@chakra-ui/anatomy'; ...@@ -2,12 +2,16 @@ import type { tagAnatomy as parts } from '@chakra-ui/anatomy';
import type { ComponentStyleConfig } from '@chakra-ui/theme'; import type { ComponentStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import type { PartsStyleFunction } 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 variantGray: PartsStyleFunction<typeof parts> = (props) => {
const transitionProps = getDefaultTransitionProps();
return { return {
container: { container: {
bg: mode('gray.200', 'gray.600')(props), bg: mode('gray.200', 'gray.600')(props),
color: mode('gray.600', 'gray.50')(props), color: mode('gray.600', 'gray.50')(props),
...transitionProps,
}, },
} }
} }
......
...@@ -3,6 +3,7 @@ import { type ThemeConfig } from '@chakra-ui/react'; ...@@ -3,6 +3,7 @@ import { type ThemeConfig } from '@chakra-ui/react';
const config: ThemeConfig = { const config: ThemeConfig = {
initialColorMode: 'system', initialColorMode: 'system',
useSystemColorMode: false, useSystemColorMode: false,
disableTransitionOnChange: false,
} }
export default config; export default config;
import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
const global = (props: StyleFunctionProps) => ({ const global = (props: StyleFunctionProps) => ({
body: { body: {
bg: mode('white', 'black')(props), 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 { ...@@ -12,13 +12,13 @@ import {
} from '@chakra-ui/system' } from '@chakra-ui/system'
import { dataAttr, __DEV__ } from '@chakra-ui/utils' import { dataAttr, __DEV__ } from '@chakra-ui/utils'
import * as React from 'react' import * as React from 'react'
import { SunIcon, MoonIcon } from '@chakra-ui/icons' import { SunIcon } from '@chakra-ui/icons'
import { useColorMode, useColorModeValue } from '@chakra-ui/react'; 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'; import styles from './ColorModeToggler.module.css';
const TRANSITION_DURATION = 150;
export interface ColorModeTogglerProps export interface ColorModeTogglerProps
extends Omit<UseCheckboxProps, 'isIndeterminate'>, extends Omit<UseCheckboxProps, 'isIndeterminate'>,
Omit<HTMLChakraProps<'label'>, keyof UseCheckboxProps>, Omit<HTMLChakraProps<'label'>, keyof UseCheckboxProps>,
...@@ -28,38 +28,34 @@ export interface ColorModeTogglerProps ...@@ -28,38 +28,34 @@ export interface ColorModeTogglerProps
export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((props, ref) => { export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((props, ref) => {
const ownProps = omitThemingProps(props); const ownProps = omitThemingProps(props);
const { toggleColorMode, colorMode } = useColorMode(); const { toggleColorMode, colorMode } = useColorMode();
const [ isOn, setMode ] = React.useState(colorMode === 'light');
const { const {
state, state,
getInputProps, getInputProps,
getCheckboxProps, getCheckboxProps,
getRootProps, getRootProps,
} = useCheckbox(ownProps); } = useCheckbox({ ...ownProps, isChecked: colorMode === 'light' });
const trackBg = useColorModeValue('blackAlpha.100', 'whiteAlpha.200') const trackBg = useColorModeValue('blackAlpha.100', 'whiteAlpha.200')
const thumbBg = useColorModeValue('white', 'black') const thumbBg = useColorModeValue('white', 'black')
const transitionProps = getDefaultTransitionProps();
const trackStyles: SystemStyleObject = React.useMemo(() => ({ const trackStyles: SystemStyleObject = React.useMemo(() => ({
bg: trackBg, bg: trackBg,
}), [ trackBg ]) ...transitionProps,
transitionDuration: '500ms',
}), [ trackBg, transitionProps ])
const thumbStyles: SystemStyleObject = React.useMemo(() => ({ const thumbStyles: SystemStyleObject = React.useMemo(() => ({
bg: thumbBg, bg: thumbBg,
transitionProperty: 'transform', ...transitionProps,
transitionDuration: `${ TRANSITION_DURATION }ms`, transitionProperty: 'background-color, transform',
}), [ thumbBg ]) transitionDuration: '500ms',
}), [ thumbBg, transitionProps ])
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 ]);
return ( return (
<chakra.label <chakra.label
{ ...getRootProps({ onChange: handleInputChange }) } { ...getRootProps({ onChange: toggleColorMode }) }
className={ styles.root } className={ styles.root }
> >
<input className={ styles.input } { ...getInputProps({}, ref) }/> <input className={ styles.input } { ...getInputProps({}, ref) }/>
...@@ -68,14 +64,25 @@ export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((prop ...@@ -68,14 +64,25 @@ export const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((prop
className={ styles.track } className={ styles.track }
__css={ trackStyles } __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 <chakra.div
className={ styles.thumb } className={ styles.thumb }
data-checked={ dataAttr(isOn) } data-checked={ dataAttr(state.isChecked) }
data-hover={ dataAttr(state.isHovered) } data-hover={ dataAttr(state.isHovered) }
__css={ thumbStyles } __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.div>
</chakra.label> </chakra.label>
) )
......
...@@ -2,6 +2,7 @@ import React from 'react'; ...@@ -2,6 +2,7 @@ import React from 'react';
import { VStack, Text, HStack, Icon, Link, useColorModeValue } from '@chakra-ui/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 ghIcon from 'icons/social/git.svg';
import twIcon from 'icons/social/tweet.svg'; import twIcon from 'icons/social/tweet.svg';
import tgIcon from 'icons/social/telega.svg'; import tgIcon from 'icons/social/telega.svg';
...@@ -20,12 +21,13 @@ const NavFooter = () => { ...@@ -20,12 +21,13 @@ const NavFooter = () => {
as="footer" as="footer"
spacing={ 8 } spacing={ 8 }
borderTop="1px solid" borderTop="1px solid"
borderColor={ useColorModeValue('gray.200', 'whiteAlpha.200') } borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
paddingTop={ 8 } paddingTop={ 8 }
w="100%" w="100%"
alignItems="baseline" alignItems="baseline"
color="gray.500" color="gray.500"
fontSize="xs" fontSize="xs"
{ ...getDefaultTransitionProps() }
> >
<HStack> <HStack>
{ SOCIAL_LINKS.map(sl => { { SOCIAL_LINKS.map(sl => {
......
...@@ -8,20 +8,35 @@ import NavFooter from './NavFooter' ...@@ -8,20 +8,35 @@ import NavFooter from './NavFooter'
import logoIcon from 'icons/logo.svg'; import logoIcon from 'icons/logo.svg';
import networksIcon from 'icons/networks.svg'; import networksIcon from 'icons/networks.svg';
import getDefaultTransitionProps from '../../theme/utils/getDefaultTransitionProps';
const Navigation = () => { const Navigation = () => {
return ( return (
<VStack <VStack
alignItems="flex-start" alignItems="flex-start"
spacing={ 12 } spacing={ 12 }
borderRight="1px solid" borderRight="1px solid"
borderColor={ useColorModeValue('gray.200', 'whiteAlpha.200') } borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
px={ 10 } px={ 10 }
py={ 12 } py={ 12 }
width="300px" width="300px"
{ ...getDefaultTransitionProps() }
> >
<HStack as="header" justifyContent="space-between" w="100%" px={ 4 } mb={ 2 } h={ 10 } alignItems="center"> <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
<Icon as={ networksIcon } width="20px" height="20px" color={ useColorModeValue('gray.500', 'white') }/> 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> </HStack>
<MainNavigation/> <MainNavigation/>
<AccountNavigation/> <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