diff --git a/src/App.web.tsx b/src/App.web.tsx
index 6c67dc28ba..cdba2c61e2 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -7,6 +7,7 @@ import {RootSiblingParent} from 'react-native-root-siblings'
import 'view/icons'
+import {ThemeProvider as Nova} from '#/view/nova'
import {init as initPersistedState} from '#/state/persisted'
import {useColorMode} from 'state/shell'
import {Shell} from 'view/shell/index'
@@ -28,11 +29,13 @@ import {
} from 'state/session'
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
import * as persisted from '#/state/persisted'
+import {useColorModeTheme} from '#/view/nova/util/useColorModeTheme'
function InnerApp() {
const {isInitialLoad, currentAccount} = useSession()
const {resumeSession} = useSessionApi()
const colorMode = useColorMode()
+ const theme = useColorModeTheme(colorMode)
// init
useEffect(() => {
@@ -44,23 +47,25 @@ function InnerApp() {
if (isInitialLoad) return null
return (
-
-
-
-
- {/* All components should be within this provider */}
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {/* All components should be within this provider */}
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/src/lib/ThemeContext.tsx b/src/lib/ThemeContext.tsx
index 483c50c42d..a728a797a9 100644
--- a/src/lib/ThemeContext.tsx
+++ b/src/lib/ThemeContext.tsx
@@ -1,13 +1,7 @@
-import {isWeb} from 'platform/detection'
import React, {ReactNode, createContext, useContext} from 'react'
-import {
- AppState,
- TextStyle,
- useColorScheme as useColorScheme_BUGGY,
- ViewStyle,
- ColorSchemeName,
-} from 'react-native'
+import {TextStyle, ViewStyle, ColorSchemeName} from 'react-native'
import {darkTheme, defaultTheme} from './themes'
+import {useColorScheme_FIXED} from '#/lib/hooks/useColorScheme_FIXED'
export type ColorScheme = 'light' | 'dark'
@@ -97,32 +91,6 @@ function getTheme(theme: ColorSchemeName) {
return theme === 'dark' ? darkTheme : defaultTheme
}
-/**
- * With RN iOS, we can only "trust" the color scheme reported while the app is
- * active. This is a workaround until the bug is fixed upstream.
- *
- * @see https://github.com/bluesky-social/social-app/pull/1417#issuecomment-1719868504
- * @see https://github.com/facebook/react-native/pull/39439
- */
-function useColorScheme_FIXED() {
- const colorScheme = useColorScheme_BUGGY()
- const [currentColorScheme, setCurrentColorScheme] =
- React.useState(colorScheme)
-
- React.useEffect(() => {
- // we don't need to be updating state on web
- if (isWeb) return
- const subscription = AppState.addEventListener('change', state => {
- const isActive = state === 'active'
- if (!isActive) return
- setCurrentColorScheme(colorScheme)
- })
- return () => subscription.remove()
- }, [colorScheme])
-
- return isWeb ? colorScheme : currentColorScheme
-}
-
export const ThemeProvider: React.FC = ({
theme,
children,
diff --git a/src/lib/hooks/useColorScheme_FIXED.ts b/src/lib/hooks/useColorScheme_FIXED.ts
new file mode 100644
index 0000000000..46b24979c5
--- /dev/null
+++ b/src/lib/hooks/useColorScheme_FIXED.ts
@@ -0,0 +1,34 @@
+import React from 'react'
+import {
+ AppState,
+ useColorScheme as useColorScheme_BUGGY,
+ ColorSchemeName,
+} from 'react-native'
+
+import {isWeb} from '#/platform/detection'
+
+/**
+ * With RN iOS, we can only "trust" the color scheme reported while the app is
+ * active. This is a workaround until the bug is fixed upstream.
+ *
+ * @see https://github.com/bluesky-social/social-app/pull/1417#issuecomment-1719868504
+ * @see https://github.com/facebook/react-native/pull/39439
+ */
+export function useColorScheme_FIXED() {
+ const colorScheme = useColorScheme_BUGGY()
+ const [currentColorScheme, setCurrentColorScheme] =
+ React.useState(colorScheme)
+
+ React.useEffect(() => {
+ // we don't need to be updating state on web
+ if (isWeb) return
+ const subscription = AppState.addEventListener('change', state => {
+ const isActive = state === 'active'
+ if (!isActive) return
+ setCurrentColorScheme(colorScheme)
+ })
+ return () => subscription.remove()
+ }, [colorScheme])
+
+ return isWeb ? colorScheme : currentColorScheme
+}
diff --git a/src/view/nova/components/Box.tsx b/src/view/nova/components/Box.tsx
new file mode 100644
index 0000000000..4743bff894
--- /dev/null
+++ b/src/view/nova/components/Box.tsx
@@ -0,0 +1,3 @@
+import {View} from 'react-native'
+import {styled} from '#/view/nova/system'
+export const Box = styled(View)
diff --git a/src/view/nova/components/Button.tsx b/src/view/nova/components/Button.tsx
new file mode 100644
index 0000000000..ae18cb7835
--- /dev/null
+++ b/src/view/nova/components/Button.tsx
@@ -0,0 +1,9 @@
+import {Pressable} from 'react-native'
+import {styled} from '#/view/nova/system'
+
+export const Button = styled(Pressable, {
+ px: 'l',
+ py: 's',
+ bg: 'l3',
+ radius: 40,
+})
diff --git a/src/view/nova/components/Typography.tsx b/src/view/nova/components/Typography.tsx
new file mode 100644
index 0000000000..627f6b8078
--- /dev/null
+++ b/src/view/nova/components/Typography.tsx
@@ -0,0 +1,74 @@
+import {Text as RNText} from 'react-native'
+import {styled} from '#/view/nova/system'
+import {web} from '#/view/nova/util/platform'
+
+export const Text = styled(RNText, {
+ color: 'l8',
+ fontSize: 's',
+})
+
+/**
+ * @see https://necolas.github.io/react-native-web/docs/accessibility/#semantic-html
+ * @see https://docs.expo.dev/develop/user-interface/fonts/
+ */
+export const H1 = styled(RNText, {
+ color: 'l8',
+ fontSize: 'l',
+ gtMobile: {
+ fontSize: 'xl',
+ },
+ ...web({
+ role: 'heading',
+ 'aria-level': 1,
+ }),
+})
+export const H2 = styled(RNText, {
+ color: 'l8',
+ fontSize: 'm',
+ gtMobile: {
+ fontSize: 'l',
+ },
+ ...web({
+ role: 'heading',
+ 'aria-level': 2,
+ }),
+})
+export const H3 = styled(RNText, {
+ color: 'l8',
+ fontSize: 'm',
+ ...web({
+ role: 'heading',
+ 'aria-level': 3,
+ }),
+})
+export const H4 = styled(RNText, {
+ color: 'l8',
+ fontSize: 's',
+ ...web({
+ role: 'heading',
+ 'aria-level': 4,
+ }),
+})
+export const H5 = styled(RNText, {
+ color: 'l8',
+ fontSize: 'xs',
+ ...web({
+ role: 'heading',
+ 'aria-level': 5,
+ }),
+})
+export const H6 = styled(RNText, {
+ color: 'l8',
+ fontSize: 'xxs',
+ ...web({
+ role: 'heading',
+ 'aria-level': 6,
+ }),
+})
+export const P = styled(RNText, {
+ color: 'l8',
+ fontSize: 's',
+ ...web({
+ role: 'paragraph',
+ }),
+})
diff --git a/src/view/nova/index.tsx b/src/view/nova/index.tsx
index 8123f0d586..34a7cdf5e2 100644
--- a/src/view/nova/index.tsx
+++ b/src/view/nova/index.tsx
@@ -1,102 +1,4 @@
-import {View, Text as RNText} from 'react-native'
-
-import {createSystem} from './lib/system'
-import {light, dark} from './themes'
-import {web} from './lib/utils'
-
-export * from './lib/utils'
-
-const {
- ThemeProvider,
- useTheme,
- useTokens,
- useBreakpoints,
- useStyle,
- useStyles,
- styled,
-} = createSystem({
- light,
- dark,
-})
-
-export {
- ThemeProvider,
- useTheme,
- useTokens,
- useBreakpoints,
- useStyle,
- useStyles,
- styled,
-}
-
-export const Box = styled(View)
-export const Text = styled(RNText, {
- color: 'l8',
- fontSize: 's',
-})
-
-/**
- * @see https://necolas.github.io/react-native-web/docs/accessibility/#semantic-html
- * @see https://docs.expo.dev/develop/user-interface/fonts/
- */
-export const H1 = styled(RNText, {
- color: 'l8',
- fontSize: 'l',
- gtMobile: {
- fontSize: 'xl',
- },
- ...web({
- role: 'heading',
- 'aria-level': 1,
- }),
-})
-export const H2 = styled(RNText, {
- color: 'l8',
- fontSize: 'm',
- gtMobile: {
- fontSize: 'l',
- },
- ...web({
- role: 'heading',
- 'aria-level': 2,
- }),
-})
-export const H3 = styled(RNText, {
- color: 'l8',
- fontSize: 'm',
- ...web({
- role: 'heading',
- 'aria-level': 3,
- }),
-})
-export const H4 = styled(RNText, {
- color: 'l8',
- fontSize: 's',
- ...web({
- role: 'heading',
- 'aria-level': 4,
- }),
-})
-export const H5 = styled(RNText, {
- color: 'l8',
- fontSize: 'xs',
- ...web({
- role: 'heading',
- 'aria-level': 5,
- }),
-})
-export const H6 = styled(RNText, {
- color: 'l8',
- fontSize: 'xxs',
- ...web({
- role: 'heading',
- 'aria-level': 6,
- }),
-})
-export const P = styled(RNText, {
- color: 'l8',
- fontSize: 's',
- ...web({
- role: 'paragraph',
- }),
-})
+export * from '#/view/nova/system'
+export * from '#/view/nova/components/Box'
+export * from '#/view/nova/components/Typography'
+export * from '#/view/nova/components/Button'
diff --git a/src/view/nova/lib/system.tsx b/src/view/nova/lib/system.tsx
index adaf7e2a8e..dc0219eb89 100644
--- a/src/view/nova/lib/system.tsx
+++ b/src/view/nova/lib/system.tsx
@@ -55,11 +55,14 @@ export function createSystem<
theme,
}: React.PropsWithChildren<{theme: ThemeName}>) => (
+ value={React.useMemo(
+ () => ({
+ themeName: theme,
+ theme: themes[theme],
+ themes,
+ }),
+ [theme],
+ )}>
{children}
)
@@ -74,7 +77,7 @@ export function createSystem<
function useBreakpoints() {
const {theme} = useTheme()
- const [breakpoints, setBreakpoints] = React.useState(
+ const [breakpoints, setBreakpoints] = React.useState(() =>
theme.getActiveBreakpoints({width: Dimensions.get('window').width}),
)
diff --git a/src/view/nova/system.ts b/src/view/nova/system.ts
new file mode 100644
index 0000000000..98e0f499b8
--- /dev/null
+++ b/src/view/nova/system.ts
@@ -0,0 +1,28 @@
+/**
+ * Library imports
+ */
+import {createSystem} from './lib/system'
+import {light, dark} from './themes'
+
+const {
+ ThemeProvider,
+ useTheme,
+ useTokens,
+ useBreakpoints,
+ useStyle,
+ useStyles,
+ styled,
+} = createSystem({
+ light,
+ dark,
+})
+
+export {
+ ThemeProvider,
+ useTheme,
+ useTokens,
+ useBreakpoints,
+ useStyle,
+ useStyles,
+ styled,
+}
diff --git a/src/view/nova/themes.ts b/src/view/nova/themes.ts
index 1f60621b75..01b3641a74 100644
--- a/src/view/nova/themes.ts
+++ b/src/view/nova/themes.ts
@@ -16,6 +16,15 @@ const palette = {
export const light = createTheme({
tokens: {
+ space: {
+ xxs: 2,
+ xs: 4,
+ s: 8,
+ m: 12,
+ l: 18,
+ xl: 24,
+ xxl: 32,
+ },
color: {
primary: palette.blue,
l1: palette.white,
@@ -118,7 +127,7 @@ export const light = createTheme({
/**
* Shorthand for applying `fontSize` and `fontHeight`, according to our type scale.
*/
- fontSize(value: 'xs' | 's' | 'm' | 'l' | 'xl', tokens) {
+ fontSize(value: 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl', tokens) {
return {
fontSize: tokens.fontSize[value],
lineHeight: tokens.lineHeight[value],
diff --git a/src/view/nova/lib/utils.ts b/src/view/nova/util/platform.ts
similarity index 100%
rename from src/view/nova/lib/utils.ts
rename to src/view/nova/util/platform.ts
diff --git a/src/view/nova/util/useColorModeTheme.ts b/src/view/nova/util/useColorModeTheme.ts
new file mode 100644
index 0000000000..4d56332b38
--- /dev/null
+++ b/src/view/nova/util/useColorModeTheme.ts
@@ -0,0 +1,9 @@
+import * as persisted from '#/state/persisted'
+import {useColorScheme_FIXED} from '#/lib/hooks/useColorScheme_FIXED'
+
+export function useColorModeTheme(
+ theme: persisted.Schema['colorMode'],
+): 'light' | 'dark' {
+ const colorScheme = useColorScheme_FIXED()
+ return (theme === 'system' ? colorScheme : theme) || 'light'
+}