Options for selecting dark theme, fix some white flashes when in dark mode (#2722)
* add dark theme selection to settings/schema
* use `useThemePrefs` where needed
* adjust theme providers to support various themes
* update storybook
* handle web themes
* better themeing for web
* dont show dark theme prefs when color mode is light
* drop the inverted text change on oled theme
* get the color mode inside of `useColorModeTheme`
* use `ThemeName` type everywhere
* typo
* use dim/dark instead of dark/oled
* prevent any fickers on web
* fix styles
* use `dim` for dark default
* more cleanup
* 🤔
* set system background color
* ts
zio/stable
parent
856f80fc6d
commit
ec86282403
|
@ -1,13 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover">
|
||||
<meta name="referrer" content="origin-when-cross-origin">
|
||||
<title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
|
||||
|
||||
<!-- Hello Humans! API docs at https://atproto.com -->
|
||||
|
||||
|
||||
<style>
|
||||
/**
|
||||
* Extend the react-native-web reset:
|
||||
|
@ -40,31 +40,39 @@
|
|||
}
|
||||
|
||||
/* Color theming */
|
||||
/* Default will always be white */
|
||||
:root {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
html.colorMode--dark {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html.colorMode--system {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
}
|
||||
/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html.colorMode--system {
|
||||
:root {
|
||||
--text: white;
|
||||
--background: black;
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite those preferences with the selected theme */
|
||||
html.theme--light {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
html.theme--dark {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--background: black;
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
html.theme--dim {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--backgroundLight: hsl(211, 20%, 10%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* Remove autofill styles on Webkit */
|
||||
|
|
|
@ -17,7 +17,6 @@ import {ThemeProvider as Alf} from '#/alf'
|
|||
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||
import {init as initPersistedState} from '#/state/persisted'
|
||||
import {listenSessionDropped} from './state/events'
|
||||
import {useColorMode} from 'state/shell'
|
||||
import {ThemeProvider} from 'lib/ThemeContext'
|
||||
import {s} from 'lib/styles'
|
||||
import {Shell} from 'view/shell'
|
||||
|
@ -49,10 +48,9 @@ import {useLingui} from '@lingui/react'
|
|||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
function InnerApp() {
|
||||
const colorMode = useColorMode()
|
||||
const {isInitialLoad, currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme(colorMode)
|
||||
const theme = useColorModeTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
// init
|
||||
|
@ -75,7 +73,7 @@ function InnerApp() {
|
|||
key={currentAccount?.did}>
|
||||
<LoggedOutViewProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<ThemeProvider theme={colorMode}>
|
||||
<ThemeProvider theme={theme}>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<GestureHandlerRootView style={s.h100pct}>
|
||||
|
|
|
@ -10,7 +10,6 @@ import 'view/icons'
|
|||
import {ThemeProvider as Alf} from '#/alf'
|
||||
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||
import {init as initPersistedState} from '#/state/persisted'
|
||||
import {useColorMode} from 'state/shell'
|
||||
import {Shell} from 'view/shell/index'
|
||||
import {ToastContainer} from 'view/com/util/Toast.web'
|
||||
import {ThemeProvider} from 'lib/ThemeContext'
|
||||
|
@ -36,8 +35,7 @@ import {Provider as PortalProvider} from '#/components/Portal'
|
|||
function InnerApp() {
|
||||
const {isInitialLoad, currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const colorMode = useColorMode()
|
||||
const theme = useColorModeTheme(colorMode)
|
||||
const theme = useColorModeTheme()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
|
@ -55,7 +53,7 @@ function InnerApp() {
|
|||
key={currentAccount?.did}>
|
||||
<LoggedOutViewProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<ThemeProvider theme={colorMode}>
|
||||
<ThemeProvider theme={theme}>
|
||||
{/* All components should be within this provider */}
|
||||
<RootSiblingParent>
|
||||
<SafeAreaProvider>
|
||||
|
|
|
@ -21,7 +21,7 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|||
import Svg, {Path, SvgProps} from 'react-native-svg'
|
||||
|
||||
import {isAndroid} from '#/platform/detection'
|
||||
import {useColorMode} from '#/state/shell'
|
||||
import {useThemePrefs} from 'state/shell'
|
||||
import {Logotype} from '#/view/icons/Logotype'
|
||||
|
||||
// @ts-ignore
|
||||
|
@ -75,7 +75,7 @@ export function Splash(props: React.PropsWithChildren<Props>) {
|
|||
isLayoutReady &&
|
||||
reduceMotion !== undefined
|
||||
|
||||
const colorMode = useColorMode()
|
||||
const {colorMode} = useThemePrefs()
|
||||
const colorScheme = useColorScheme()
|
||||
const themeName = colorMode === 'system' ? colorScheme : colorMode
|
||||
const isDarkMode = themeName === 'dark'
|
||||
|
|
|
@ -71,7 +71,7 @@ export const lightPalette = {
|
|||
|
||||
export const darkPalette: Palette = {
|
||||
white: tokens.color.gray_0,
|
||||
black: tokens.color.gray_1000,
|
||||
black: tokens.color.trueBlack,
|
||||
|
||||
contrast_25: tokens.color.gray_975,
|
||||
contrast_50: tokens.color.gray_950,
|
||||
|
@ -130,6 +130,11 @@ export const darkPalette: Palette = {
|
|||
negative_975: tokens.color.red_975,
|
||||
} as const
|
||||
|
||||
export const dimPalette: Palette = {
|
||||
...darkPalette,
|
||||
black: tokens.color.gray_1000,
|
||||
} as const
|
||||
|
||||
export const light = {
|
||||
name: 'light',
|
||||
palette: lightPalette,
|
||||
|
@ -191,70 +196,6 @@ export const light = {
|
|||
},
|
||||
}
|
||||
|
||||
export const dim: Theme = {
|
||||
name: 'dim',
|
||||
palette: darkPalette,
|
||||
atoms: {
|
||||
text: {
|
||||
color: darkPalette.white,
|
||||
},
|
||||
text_contrast_700: {
|
||||
color: darkPalette.contrast_800,
|
||||
},
|
||||
text_contrast_600: {
|
||||
color: darkPalette.contrast_700,
|
||||
},
|
||||
text_contrast_500: {
|
||||
color: darkPalette.contrast_600,
|
||||
},
|
||||
text_contrast_400: {
|
||||
color: darkPalette.contrast_500,
|
||||
},
|
||||
text_inverted: {
|
||||
color: darkPalette.black,
|
||||
},
|
||||
bg: {
|
||||
backgroundColor: darkPalette.contrast_50,
|
||||
},
|
||||
bg_contrast_25: {
|
||||
backgroundColor: darkPalette.contrast_100,
|
||||
},
|
||||
bg_contrast_50: {
|
||||
backgroundColor: darkPalette.contrast_200,
|
||||
},
|
||||
bg_contrast_100: {
|
||||
backgroundColor: darkPalette.contrast_300,
|
||||
},
|
||||
bg_contrast_200: {
|
||||
backgroundColor: darkPalette.contrast_400,
|
||||
},
|
||||
bg_contrast_300: {
|
||||
backgroundColor: darkPalette.contrast_500,
|
||||
},
|
||||
border: {
|
||||
borderColor: darkPalette.contrast_200,
|
||||
},
|
||||
border_contrast: {
|
||||
borderColor: darkPalette.contrast_400,
|
||||
},
|
||||
shadow_sm: {
|
||||
...atoms.shadow_sm,
|
||||
shadowOpacity: 0.7,
|
||||
shadowColor: tokens.color.trueBlack,
|
||||
},
|
||||
shadow_md: {
|
||||
...atoms.shadow_md,
|
||||
shadowOpacity: 0.7,
|
||||
shadowColor: tokens.color.trueBlack,
|
||||
},
|
||||
shadow_lg: {
|
||||
...atoms.shadow_lg,
|
||||
shadowOpacity: 0.7,
|
||||
shadowColor: tokens.color.trueBlack,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const dark: Theme = {
|
||||
name: 'dark',
|
||||
palette: darkPalette,
|
||||
|
@ -318,3 +259,17 @@ export const dark: Theme = {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const dim: Theme = {
|
||||
...dark,
|
||||
name: 'dim',
|
||||
atoms: {
|
||||
...dark.atoms,
|
||||
text_inverted: {
|
||||
color: dimPalette.black,
|
||||
},
|
||||
bg: {
|
||||
backgroundColor: dimPalette.black,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,10 +1,54 @@
|
|||
import React from 'react'
|
||||
import {useColorScheme} from 'react-native'
|
||||
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {useThemePrefs} from 'state/shell'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {ThemeName, light, dark, dim} from '#/alf/themes'
|
||||
import * as SystemUI from 'expo-system-ui'
|
||||
|
||||
export function useColorModeTheme(
|
||||
theme: persisted.Schema['colorMode'],
|
||||
): 'light' | 'dark' {
|
||||
export function useColorModeTheme(): ThemeName {
|
||||
const colorScheme = useColorScheme()
|
||||
return (theme === 'system' ? colorScheme : theme) || 'light'
|
||||
const {colorMode, darkTheme} = useThemePrefs()
|
||||
|
||||
return React.useMemo(() => {
|
||||
if (
|
||||
(colorMode === 'system' && colorScheme === 'light') ||
|
||||
colorMode === 'light'
|
||||
) {
|
||||
updateDocument('light')
|
||||
updateSystemBackground('light')
|
||||
return 'light'
|
||||
} else {
|
||||
const themeName = darkTheme ?? 'dim'
|
||||
updateDocument(themeName)
|
||||
updateSystemBackground(themeName)
|
||||
return themeName
|
||||
}
|
||||
}, [colorMode, darkTheme, colorScheme])
|
||||
}
|
||||
|
||||
function updateDocument(theme: ThemeName) {
|
||||
// @ts-ignore web only
|
||||
if (isWeb && typeof window !== 'undefined') {
|
||||
// @ts-ignore web only
|
||||
const html = window.document.documentElement
|
||||
// remove any other color mode classes
|
||||
html.className = html.className.replace(/(theme)--\w+/g, '')
|
||||
|
||||
html.classList.add(`theme--${theme}`)
|
||||
}
|
||||
}
|
||||
|
||||
function updateSystemBackground(theme: ThemeName) {
|
||||
switch (theme) {
|
||||
case 'light':
|
||||
SystemUI.setBackgroundColorAsync(light.atoms.bg.backgroundColor)
|
||||
break
|
||||
case 'dark':
|
||||
SystemUI.setBackgroundColorAsync(dark.atoms.bg.backgroundColor)
|
||||
break
|
||||
case 'dim':
|
||||
SystemUI.setBackgroundColorAsync(dim.atoms.bg.backgroundColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import React, {ReactNode, createContext, useContext} from 'react'
|
||||
import {
|
||||
TextStyle,
|
||||
useColorScheme,
|
||||
ViewStyle,
|
||||
ColorSchemeName,
|
||||
} from 'react-native'
|
||||
import {darkTheme, defaultTheme} from './themes'
|
||||
import {TextStyle, ViewStyle} from 'react-native'
|
||||
import {darkTheme, defaultTheme, dimTheme} from './themes'
|
||||
import {ThemeName} from '#/alf/themes'
|
||||
|
||||
export type ColorScheme = 'light' | 'dark'
|
||||
|
||||
|
@ -84,23 +80,31 @@ export interface Theme {
|
|||
|
||||
export interface ThemeProviderProps {
|
||||
children?: ReactNode
|
||||
theme?: 'light' | 'dark' | 'system'
|
||||
theme: ThemeName
|
||||
}
|
||||
|
||||
export const ThemeContext = createContext<Theme>(defaultTheme)
|
||||
|
||||
export const useTheme = () => useContext(ThemeContext)
|
||||
|
||||
function getTheme(theme: ColorSchemeName) {
|
||||
return theme === 'dark' ? darkTheme : defaultTheme
|
||||
function getTheme(theme: ThemeName) {
|
||||
switch (theme) {
|
||||
case 'light':
|
||||
return defaultTheme
|
||||
case 'dim':
|
||||
return dimTheme
|
||||
case 'dark':
|
||||
return darkTheme
|
||||
default:
|
||||
return defaultTheme
|
||||
}
|
||||
}
|
||||
|
||||
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
||||
theme,
|
||||
children,
|
||||
}) => {
|
||||
const colorScheme = useColorScheme()
|
||||
const themeValue = getTheme(theme === 'system' ? colorScheme : theme)
|
||||
const themeValue = getTheme(theme)
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={themeValue}>{children}</ThemeContext.Provider>
|
||||
|
|
|
@ -2,7 +2,7 @@ import {Platform} from 'react-native'
|
|||
import type {Theme} from './ThemeContext'
|
||||
import {colors} from './styles'
|
||||
|
||||
import {darkPalette, lightPalette} from '#/alf/themes'
|
||||
import {darkPalette, lightPalette, dimPalette} from '#/alf/themes'
|
||||
|
||||
export const defaultTheme: Theme = {
|
||||
colorScheme: 'light',
|
||||
|
@ -336,3 +336,14 @@ export const darkTheme: Theme = {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const dimTheme: Theme = {
|
||||
...darkTheme,
|
||||
palette: {
|
||||
...darkTheme.palette,
|
||||
default: {
|
||||
...darkTheme.palette.default,
|
||||
background: dimPalette.black,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ const DEPRECATED_ROOT_STATE_STORAGE_KEY = 'root'
|
|||
export function transform(legacy: Partial<LegacySchema>): Schema {
|
||||
return {
|
||||
colorMode: legacy.shell?.colorMode || defaults.colorMode,
|
||||
darkTheme: defaults.darkTheme,
|
||||
session: {
|
||||
accounts: legacy.session?.accounts || defaults.session.accounts,
|
||||
currentAccount:
|
||||
|
|
|
@ -18,6 +18,7 @@ export type PersistedAccount = z.infer<typeof accountSchema>
|
|||
|
||||
export const schema = z.object({
|
||||
colorMode: z.enum(['system', 'light', 'dark']),
|
||||
darkTheme: z.enum(['dim', 'dark']).optional(),
|
||||
session: z.object({
|
||||
accounts: z.array(accountSchema),
|
||||
currentAccount: accountSchema.optional(),
|
||||
|
@ -60,6 +61,7 @@ export type Schema = z.infer<typeof schema>
|
|||
|
||||
export const defaults: Schema = {
|
||||
colorMode: 'system',
|
||||
darkTheme: 'dim',
|
||||
session: {
|
||||
accounts: [],
|
||||
currentAccount: undefined,
|
||||
|
|
|
@ -1,57 +1,65 @@
|
|||
import React from 'react'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import * as persisted from '#/state/persisted'
|
||||
|
||||
type StateContext = persisted.Schema['colorMode']
|
||||
type SetContext = (v: persisted.Schema['colorMode']) => void
|
||||
type StateContext = {
|
||||
colorMode: persisted.Schema['colorMode']
|
||||
darkTheme: persisted.Schema['darkTheme']
|
||||
}
|
||||
type SetContext = {
|
||||
setColorMode: (v: persisted.Schema['colorMode']) => void
|
||||
setDarkTheme: (v: persisted.Schema['darkTheme']) => void
|
||||
}
|
||||
|
||||
const stateContext = React.createContext<StateContext>('system')
|
||||
const setContext = React.createContext<SetContext>(
|
||||
(_: persisted.Schema['colorMode']) => {},
|
||||
)
|
||||
const stateContext = React.createContext<StateContext>({
|
||||
colorMode: 'system',
|
||||
darkTheme: 'dark',
|
||||
})
|
||||
const setContext = React.createContext<SetContext>({} as SetContext)
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [state, setState] = React.useState(persisted.get('colorMode'))
|
||||
const [colorMode, setColorMode] = React.useState(persisted.get('colorMode'))
|
||||
const [darkTheme, setDarkTheme] = React.useState(persisted.get('darkTheme'))
|
||||
|
||||
const setStateWrapped = React.useCallback(
|
||||
(colorMode: persisted.Schema['colorMode']) => {
|
||||
setState(colorMode)
|
||||
persisted.write('colorMode', colorMode)
|
||||
updateDocument(colorMode)
|
||||
const setColorModeWrapped = React.useCallback(
|
||||
(_colorMode: persisted.Schema['colorMode']) => {
|
||||
setColorMode(_colorMode)
|
||||
persisted.write('colorMode', _colorMode)
|
||||
},
|
||||
[setState],
|
||||
[setColorMode],
|
||||
)
|
||||
|
||||
const setDarkThemeWrapped = React.useCallback(
|
||||
(_darkTheme: persisted.Schema['darkTheme']) => {
|
||||
setDarkTheme(_darkTheme)
|
||||
persisted.write('darkTheme', _darkTheme)
|
||||
},
|
||||
[setDarkTheme],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
updateDocument(persisted.get('colorMode')) // set on load
|
||||
return persisted.onUpdate(() => {
|
||||
setState(persisted.get('colorMode'))
|
||||
updateDocument(persisted.get('colorMode'))
|
||||
setColorModeWrapped(persisted.get('colorMode'))
|
||||
setDarkThemeWrapped(persisted.get('darkTheme'))
|
||||
})
|
||||
}, [setState])
|
||||
}, [setColorModeWrapped, setDarkThemeWrapped])
|
||||
|
||||
return (
|
||||
<stateContext.Provider value={state}>
|
||||
<setContext.Provider value={setStateWrapped}>
|
||||
<stateContext.Provider value={{colorMode, darkTheme}}>
|
||||
<setContext.Provider
|
||||
value={{
|
||||
setDarkTheme: setDarkThemeWrapped,
|
||||
setColorMode: setColorModeWrapped,
|
||||
}}>
|
||||
{children}
|
||||
</setContext.Provider>
|
||||
</stateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useColorMode() {
|
||||
export function useThemePrefs() {
|
||||
return React.useContext(stateContext)
|
||||
}
|
||||
|
||||
export function useSetColorMode() {
|
||||
export function useSetThemePrefs() {
|
||||
return React.useContext(setContext)
|
||||
}
|
||||
|
||||
function updateDocument(colorMode: string) {
|
||||
if (isWeb && typeof window !== 'undefined') {
|
||||
const html = window.document.documentElement
|
||||
// remove any other color mode classes
|
||||
html.className = html.className.replace(/colorMode--\w+/g, '')
|
||||
html.classList.add(`colorMode--${colorMode}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export {
|
|||
useSetDrawerSwipeDisabled,
|
||||
} from './drawer-swipe-disabled'
|
||||
export {useMinimalShellMode, useSetMinimalShellMode} from './minimal-mode'
|
||||
export {useColorMode, useSetColorMode} from './color-mode'
|
||||
export {useThemePrefs, useSetThemePrefs} from './color-mode'
|
||||
export {useOnboardingState, useOnboardingDispatch} from './onboarding'
|
||||
export {useComposerState, useComposerControls} from './composer'
|
||||
export {useTickEveryMinute} from './tick-every-minute'
|
||||
|
|
|
@ -40,8 +40,8 @@ import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
|
|||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useSetMinimalShellMode,
|
||||
useColorMode,
|
||||
useSetColorMode,
|
||||
useThemePrefs,
|
||||
useSetThemePrefs,
|
||||
useOnboardingDispatch,
|
||||
} from '#/state/shell'
|
||||
import {
|
||||
|
@ -144,8 +144,8 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
|
|||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
||||
export function SettingsScreen({}: Props) {
|
||||
const queryClient = useQueryClient()
|
||||
const colorMode = useColorMode()
|
||||
const setColorMode = useSetColorMode()
|
||||
const {colorMode, darkTheme} = useThemePrefs()
|
||||
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
@ -483,8 +483,36 @@ export function SettingsScreen({}: Props) {
|
|||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.spacer20} />
|
||||
|
||||
{colorMode !== 'light' && (
|
||||
<>
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Dark Theme</Trans>
|
||||
</Text>
|
||||
<View>
|
||||
<View style={[styles.linkCard, pal.view, styles.selectableBtns]}>
|
||||
<SelectableBtn
|
||||
selected={!darkTheme || darkTheme === 'dim'}
|
||||
label={_(msg`Dim`)}
|
||||
left
|
||||
onSelect={() => setDarkTheme('dim')}
|
||||
accessibilityHint={_(msg`Set dark theme to the dim theme`)}
|
||||
/>
|
||||
<SelectableBtn
|
||||
selected={darkTheme === 'dark'}
|
||||
label={_(msg`Dark`)}
|
||||
right
|
||||
onSelect={() => setDarkTheme('dark')}
|
||||
accessibilityHint={_(msg`Set dark theme to the dark theme`)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.spacer20} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Basics</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -3,7 +3,7 @@ import {View} from 'react-native'
|
|||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||
|
||||
import {atoms as a, useTheme, ThemeProvider} from '#/alf'
|
||||
import {useSetColorMode} from '#/state/shell'
|
||||
import {useSetThemePrefs} from '#/state/shell'
|
||||
import {Button} from '#/components/Button'
|
||||
|
||||
import {Theming} from './Theming'
|
||||
|
@ -19,7 +19,7 @@ import {Icons} from './Icons'
|
|||
|
||||
export function Storybook() {
|
||||
const t = useTheme()
|
||||
const setColorMode = useSetColorMode()
|
||||
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
|
@ -38,7 +38,7 @@ export function Storybook() {
|
|||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "system"'
|
||||
label='Set theme to "light"'
|
||||
onPress={() => setColorMode('light')}>
|
||||
Light
|
||||
</Button>
|
||||
|
@ -46,8 +46,22 @@ export function Storybook() {
|
|||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "system"'
|
||||
onPress={() => setColorMode('dark')}>
|
||||
label='Set theme to "dim"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dim')
|
||||
}}>
|
||||
Dim
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "dark"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dark')
|
||||
}}>
|
||||
Dark
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<!--
|
||||
<!--
|
||||
This viewport works for phones with notches.
|
||||
It's optimized for gestures by disabling global zoom.
|
||||
-->
|
||||
|
@ -44,31 +44,39 @@
|
|||
}
|
||||
|
||||
/* Color theming */
|
||||
/* Default will always be white */
|
||||
:root {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
html.colorMode--dark {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html.colorMode--system {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
}
|
||||
/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html.colorMode--system {
|
||||
:root {
|
||||
--text: white;
|
||||
--background: black;
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite those preferences with the selected theme */
|
||||
html.theme--light {
|
||||
--text: black;
|
||||
--background: white;
|
||||
--backgroundLight: hsl(211, 20%, 95%);
|
||||
}
|
||||
html.theme--dark {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--background: black;
|
||||
--backgroundLight: hsl(211, 20%, 20%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
html.theme--dim {
|
||||
--text: white;
|
||||
--background: hsl(211, 20%, 4%);
|
||||
--backgroundLight: hsl(211, 20%, 10%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* Remove autofill styles on Webkit */
|
||||
|
@ -142,7 +150,7 @@
|
|||
.ProseMirror .mention {
|
||||
color: #0085ff;
|
||||
}
|
||||
.ProseMirror a,
|
||||
.ProseMirror a,
|
||||
.ProseMirror .autolink {
|
||||
color: #0085ff;
|
||||
}
|
||||
|
@ -200,7 +208,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<!--
|
||||
<!--
|
||||
A generic no script element with a reload button and a message.
|
||||
Feel free to customize this however you'd like.
|
||||
-->
|
||||
|
|
Loading…
Reference in New Issue