[ALF] Theme & palette cleanup (#4769)

* Invert primary scale

* Invert negative palette

* Replace theme specific styles in Toggle

* Remove theme specific colors from Button, improves secondary solid on dark mode

* TextField

* Remove from MessageItem

* Threadgate editor

* IconCircle

* Muted words

* Generate themes from hues

* Cleanup

* Deprecate more values, fix circular import

* Invert positive too, hardly use

* Button tweaks, some theme diffs

* Match disabled state for negative button

* Fix unread noty bg
zio/stable
Eric Bailey 2024-07-11 16:59:12 -05:00 committed by GitHub
parent ea0586cd67
commit 74186950b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 851 additions and 654 deletions

View File

@ -1,7 +1,9 @@
import React from 'react' import React from 'react'
import {Dimensions} from 'react-native' import {Dimensions} from 'react-native'
import * as themes from '#/alf/themes' import {createThemes, defaultTheme} from '#/alf/themes'
import {Theme, ThemeName} from '#/alf/types'
import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
export {atoms} from '#/alf/atoms' export {atoms} from '#/alf/atoms'
export * as tokens from '#/alf/tokens' export * as tokens from '#/alf/tokens'
@ -39,8 +41,8 @@ function getActiveBreakpoints({width}: {width: number}) {
* Context * Context
*/ */
export const Context = React.createContext<{ export const Context = React.createContext<{
themeName: themes.ThemeName themeName: ThemeName
theme: themes.Theme theme: Theme
breakpoints: { breakpoints: {
active: BreakpointName | undefined active: BreakpointName | undefined
gtPhone: boolean gtPhone: boolean
@ -49,7 +51,7 @@ export const Context = React.createContext<{
} }
}>({ }>({
themeName: 'light', themeName: 'light',
theme: themes.light, theme: defaultTheme,
breakpoints: { breakpoints: {
active: undefined, active: undefined,
gtPhone: false, gtPhone: false,
@ -61,7 +63,16 @@ export const Context = React.createContext<{
export function ThemeProvider({ export function ThemeProvider({
children, children,
theme: themeName, theme: themeName,
}: React.PropsWithChildren<{theme: themes.ThemeName}>) { }: React.PropsWithChildren<{theme: ThemeName}>) {
const themes = React.useMemo(() => {
return createThemes({
hues: {
primary: BLUE_HUE,
negative: RED_HUE,
positive: GREEN_HUE,
},
})
}, [])
const theme = themes[themeName] const theme = themes[themeName]
const [breakpoints, setBreakpoints] = React.useState(() => const [breakpoints, setBreakpoints] = React.useState(() =>
getActiveBreakpoints({width: Dimensions.get('window').width}), getActiveBreakpoints({width: Dimensions.get('window').width}),

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +1,6 @@
import {
BLUE_HUE,
generateScale,
GREEN_HUE,
RED_HUE,
} from '#/alf/util/colorGeneration'
export const scale = generateScale(6, 100)
// dim shifted 6% lighter
export const dimScale = generateScale(12, 100)
export const color = { export const color = {
trueBlack: '#000000',
temp_purple: 'rgb(105 0 255)', temp_purple: 'rgb(105 0 255)',
temp_purple_dark: 'rgb(83 0 202)', temp_purple_dark: 'rgb(83 0 202)',
gray_0: `hsl(${BLUE_HUE}, 20%, ${scale[14]}%)`,
gray_25: `hsl(${BLUE_HUE}, 20%, ${scale[13]}%)`,
gray_50: `hsl(${BLUE_HUE}, 20%, ${scale[12]}%)`,
gray_100: `hsl(${BLUE_HUE}, 20%, ${scale[11]}%)`,
gray_200: `hsl(${BLUE_HUE}, 20%, ${scale[10]}%)`,
gray_300: `hsl(${BLUE_HUE}, 20%, ${scale[9]}%)`,
gray_400: `hsl(${BLUE_HUE}, 20%, ${scale[8]}%)`,
gray_500: `hsl(${BLUE_HUE}, 20%, ${scale[7]}%)`,
gray_600: `hsl(${BLUE_HUE}, 24%, ${scale[6]}%)`,
gray_700: `hsl(${BLUE_HUE}, 24%, ${scale[5]}%)`,
gray_800: `hsl(${BLUE_HUE}, 28%, ${scale[4]}%)`,
gray_900: `hsl(${BLUE_HUE}, 28%, ${scale[3]}%)`,
gray_950: `hsl(${BLUE_HUE}, 28%, ${scale[2]}%)`,
gray_975: `hsl(${BLUE_HUE}, 28%, ${scale[1]}%)`,
gray_1000: `hsl(${BLUE_HUE}, 28%, ${scale[0]}%)`,
blue_25: `hsl(${BLUE_HUE}, 99%, 97%)`,
blue_50: `hsl(${BLUE_HUE}, 99%, 95%)`,
blue_100: `hsl(${BLUE_HUE}, 99%, 90%)`,
blue_200: `hsl(${BLUE_HUE}, 99%, 80%)`,
blue_300: `hsl(${BLUE_HUE}, 99%, 70%)`,
blue_400: `hsl(${BLUE_HUE}, 99%, 60%)`,
blue_500: `hsl(${BLUE_HUE}, 99%, 53%)`,
blue_600: `hsl(${BLUE_HUE}, 99%, 42%)`,
blue_700: `hsl(${BLUE_HUE}, 99%, 34%)`,
blue_800: `hsl(${BLUE_HUE}, 99%, 26%)`,
blue_900: `hsl(${BLUE_HUE}, 99%, 18%)`,
blue_950: `hsl(${BLUE_HUE}, 99%, 10%)`,
blue_975: `hsl(${BLUE_HUE}, 99%, 7%)`,
green_25: `hsl(${GREEN_HUE}, 82%, 97%)`,
green_50: `hsl(${GREEN_HUE}, 82%, 95%)`,
green_100: `hsl(${GREEN_HUE}, 82%, 90%)`,
green_200: `hsl(${GREEN_HUE}, 82%, 80%)`,
green_300: `hsl(${GREEN_HUE}, 82%, 70%)`,
green_400: `hsl(${GREEN_HUE}, 82%, 60%)`,
green_500: `hsl(${GREEN_HUE}, 82%, 50%)`,
green_600: `hsl(${GREEN_HUE}, 82%, 42%)`,
green_700: `hsl(${GREEN_HUE}, 82%, 34%)`,
green_800: `hsl(${GREEN_HUE}, 82%, 26%)`,
green_900: `hsl(${GREEN_HUE}, 82%, 18%)`,
green_950: `hsl(${GREEN_HUE}, 82%, 10%)`,
green_975: `hsl(${GREEN_HUE}, 82%, 7%)`,
red_25: `hsl(${RED_HUE}, 91%, 97%)`,
red_50: `hsl(${RED_HUE}, 91%, 95%)`,
red_100: `hsl(${RED_HUE}, 91%, 90%)`,
red_200: `hsl(${RED_HUE}, 91%, 80%)`,
red_300: `hsl(${RED_HUE}, 91%, 70%)`,
red_400: `hsl(${RED_HUE}, 91%, 60%)`,
red_500: `hsl(${RED_HUE}, 91%, 50%)`,
red_600: `hsl(${RED_HUE}, 91%, 42%)`,
red_700: `hsl(${RED_HUE}, 91%, 34%)`,
red_800: `hsl(${RED_HUE}, 91%, 26%)`,
red_900: `hsl(${RED_HUE}, 91%, 18%)`,
red_950: `hsl(${RED_HUE}, 91%, 10%)`,
red_975: `hsl(${RED_HUE}, 91%, 7%)`,
} as const } as const
export const space = { export const space = {
@ -178,10 +107,3 @@ export const gradients = {
hover_value: '#755B62', hover_value: '#755B62',
}, },
} as const } as const
export type Color = keyof typeof color
export type Space = keyof typeof space
export type FontSize = keyof typeof fontSize
export type LineHeight = keyof typeof lineHeight
export type BorderRadius = keyof typeof borderRadius
export type FontWeight = keyof typeof fontWeight

View File

@ -1,21 +1,4 @@
import {StyleProp, ViewStyle, TextStyle} from 'react-native' import {StyleProp, TextStyle, ViewStyle} from 'react-native'
type LiteralToCommon<T extends PropertyKey> = T extends number
? number
: T extends string
? string
: T extends symbol
? symbol
: never
/**
* @see https://stackoverflow.com/questions/68249999/use-as-const-in-typescript-without-adding-readonly-modifiers
*/
export type Mutable<T> = {
-readonly [K in keyof T]: T[K] extends PropertyKey
? LiteralToCommon<T[K]>
: Mutable<T[K]>
}
export type TextStyleProp = { export type TextStyleProp = {
style?: StyleProp<TextStyle> style?: StyleProp<TextStyle>
@ -24,3 +7,156 @@ export type TextStyleProp = {
export type ViewStyleProp = { export type ViewStyleProp = {
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
} }
export type ThemeName = 'light' | 'dim' | 'dark'
export type Palette = {
white: string
black: string
contrast_25: string
contrast_50: string
contrast_100: string
contrast_200: string
contrast_300: string
contrast_400: string
contrast_500: string
contrast_600: string
contrast_700: string
contrast_800: string
contrast_900: string
contrast_950: string
contrast_975: string
primary_25: string
primary_50: string
primary_100: string
primary_200: string
primary_300: string
primary_400: string
primary_500: string
primary_600: string
primary_700: string
primary_800: string
primary_900: string
primary_950: string
primary_975: string
positive_25: string
positive_50: string
positive_100: string
positive_200: string
positive_300: string
positive_400: string
positive_500: string
positive_600: string
positive_700: string
positive_800: string
positive_900: string
positive_950: string
positive_975: string
negative_25: string
negative_50: string
negative_100: string
negative_200: string
negative_300: string
negative_400: string
negative_500: string
negative_600: string
negative_700: string
negative_800: string
negative_900: string
negative_950: string
negative_975: string
}
export type ThemedAtoms = {
text: {
color: string
}
text_contrast_low: {
color: string
}
text_contrast_medium: {
color: string
}
text_contrast_high: {
color: string
}
text_inverted: {
color: string
}
bg: {
backgroundColor: string
}
bg_contrast_25: {
backgroundColor: string
}
bg_contrast_50: {
backgroundColor: string
}
bg_contrast_100: {
backgroundColor: string
}
bg_contrast_200: {
backgroundColor: string
}
bg_contrast_300: {
backgroundColor: string
}
bg_contrast_400: {
backgroundColor: string
}
bg_contrast_500: {
backgroundColor: string
}
bg_contrast_600: {
backgroundColor: string
}
bg_contrast_700: {
backgroundColor: string
}
bg_contrast_800: {
backgroundColor: string
}
bg_contrast_900: {
backgroundColor: string
}
bg_contrast_950: {
backgroundColor: string
}
bg_contrast_975: {
backgroundColor: string
}
border_contrast_low: {
borderColor: string
}
border_contrast_medium: {
borderColor: string
}
border_contrast_high: {
borderColor: string
}
shadow_sm: {
shadowRadius: number
shadowOpacity: number
elevation: number
shadowColor: string
}
shadow_md: {
shadowRadius: number
shadowOpacity: number
elevation: number
shadowColor: string
}
shadow_lg: {
shadowRadius: number
shadowOpacity: number
elevation: number
shadowColor: string
}
}
export type Theme = {
name: ThemeName
palette: Palette
atoms: ThemedAtoms
}

View File

@ -15,3 +15,7 @@ export function generateScale(start: number, end: number) {
return start + range * stop return start + range * stop
}) })
} }
export const defaultScale = generateScale(6, 100)
// dim shifted 6% lighter
export const dimScale = generateScale(12, 100)

View File

@ -1,4 +1,4 @@
import {ThemeName} from '#/alf/themes' import {ThemeName} from '#/alf/types'
export function select<T>(name: ThemeName, options: Record<ThemeName, T>) { export function select<T>(name: ThemeName, options: Record<ThemeName, T>) {
switch (name) { switch (name) {

View File

@ -4,7 +4,8 @@ import * as SystemUI from 'expo-system-ui'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {useThemePrefs} from 'state/shell' import {useThemePrefs} from 'state/shell'
import {dark, dim, light, ThemeName} from '#/alf/themes' import {dark, dim, light} from '#/alf/themes'
import {ThemeName} from '#/alf/types'
export function useColorModeTheme(): ThemeName { export function useColorModeTheme(): ThemeName {
const theme = useThemeName() const theme = useThemeName()

View File

@ -13,7 +13,7 @@ import {
} from 'react-native' } from 'react-native'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {android, atoms as a, flatten, tokens, useTheme} from '#/alf' import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf'
import {Props as SVGIconProps} from '#/components/icons/common' import {Props as SVGIconProps} from '#/components/icons/common'
import {normalizeTextStyles} from '#/components/Typography' import {normalizeTextStyles} from '#/components/Typography'
@ -152,7 +152,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
const {baseStyles, hoverStyles} = React.useMemo(() => { const {baseStyles, hoverStyles} = React.useMemo(() => {
const baseStyles: ViewStyle[] = [] const baseStyles: ViewStyle[] = []
const hoverStyles: ViewStyle[] = [] const hoverStyles: ViewStyle[] = []
const light = t.name === 'light'
if (color === 'primary') { if (color === 'primary') {
if (variant === 'solid') { if (variant === 'solid') {
@ -165,7 +164,11 @@ export const Button = React.forwardRef<View, ButtonProps>(
}) })
} else { } else {
baseStyles.push({ baseStyles.push({
backgroundColor: t.palette.primary_700, backgroundColor: select(t.name, {
light: t.palette.primary_700,
dim: t.palette.primary_300,
dark: t.palette.primary_300,
}),
}) })
} }
} else if (variant === 'outline') { } else if (variant === 'outline') {
@ -178,24 +181,18 @@ export const Button = React.forwardRef<View, ButtonProps>(
borderColor: t.palette.primary_500, borderColor: t.palette.primary_500,
}) })
hoverStyles.push(a.border, { hoverStyles.push(a.border, {
backgroundColor: light backgroundColor: t.palette.primary_50,
? t.palette.primary_50
: t.palette.primary_950,
}) })
} else { } else {
baseStyles.push(a.border, { baseStyles.push(a.border, {
borderColor: light borderColor: t.palette.primary_200,
? t.palette.primary_200
: t.palette.primary_900,
}) })
} }
} else if (variant === 'ghost') { } else if (variant === 'ghost') {
if (!disabled) { if (!disabled) {
baseStyles.push(t.atoms.bg) baseStyles.push(t.atoms.bg)
hoverStyles.push({ hoverStyles.push({
backgroundColor: light backgroundColor: t.palette.primary_100,
? t.palette.primary_100
: t.palette.primary_900,
}) })
} }
} }
@ -203,14 +200,26 @@ export const Button = React.forwardRef<View, ButtonProps>(
if (variant === 'solid') { if (variant === 'solid') {
if (!disabled) { if (!disabled) {
baseStyles.push({ baseStyles.push({
backgroundColor: t.palette.contrast_25, backgroundColor: select(t.name, {
light: t.palette.contrast_25,
dim: t.palette.contrast_100,
dark: t.palette.contrast_100,
}),
}) })
hoverStyles.push({ hoverStyles.push({
backgroundColor: t.palette.contrast_50, backgroundColor: select(t.name, {
light: t.palette.contrast_50,
dim: t.palette.contrast_200,
dark: t.palette.contrast_200,
}),
}) })
} else { } else {
baseStyles.push({ baseStyles.push({
backgroundColor: t.palette.contrast_100, backgroundColor: select(t.name, {
light: t.palette.contrast_100,
dim: t.palette.contrast_25,
dark: t.palette.contrast_25,
}),
}) })
} }
} else if (variant === 'outline') { } else if (variant === 'outline') {
@ -247,7 +256,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
}) })
} else { } else {
baseStyles.push({ baseStyles.push({
backgroundColor: t.palette.contrast_700, backgroundColor: t.palette.contrast_600,
}) })
} }
} else if (variant === 'outline') { } else if (variant === 'outline') {
@ -284,7 +293,11 @@ export const Button = React.forwardRef<View, ButtonProps>(
}) })
} else { } else {
baseStyles.push({ baseStyles.push({
backgroundColor: t.palette.negative_700, backgroundColor: select(t.name, {
light: t.palette.negative_700,
dim: t.palette.negative_300,
dark: t.palette.negative_300,
}),
}) })
} }
} else if (variant === 'outline') { } else if (variant === 'outline') {
@ -297,24 +310,18 @@ export const Button = React.forwardRef<View, ButtonProps>(
borderColor: t.palette.negative_500, borderColor: t.palette.negative_500,
}) })
hoverStyles.push(a.border, { hoverStyles.push(a.border, {
backgroundColor: light backgroundColor: t.palette.negative_50,
? t.palette.negative_50
: t.palette.negative_975,
}) })
} else { } else {
baseStyles.push(a.border, { baseStyles.push(a.border, {
borderColor: light borderColor: t.palette.negative_200,
? t.palette.negative_200
: t.palette.negative_900,
}) })
} }
} else if (variant === 'ghost') { } else if (variant === 'ghost') {
if (!disabled) { if (!disabled) {
baseStyles.push(t.atoms.bg) baseStyles.push(t.atoms.bg)
hoverStyles.push({ hoverStyles.push({
backgroundColor: light backgroundColor: t.palette.negative_100,
? t.palette.negative_100
: t.palette.negative_975,
}) })
} }
} }
@ -482,7 +489,6 @@ export function useSharedButtonTextStyles() {
const {color, variant, disabled, size} = useButtonContext() const {color, variant, disabled, size} = useButtonContext()
return React.useMemo(() => { return React.useMemo(() => {
const baseStyles: TextStyle[] = [] const baseStyles: TextStyle[] = []
const light = t.name === 'light'
if (color === 'primary') { if (color === 'primary') {
if (variant === 'solid') { if (variant === 'solid') {
@ -494,7 +500,7 @@ export function useSharedButtonTextStyles() {
} else if (variant === 'outline') { } else if (variant === 'outline') {
if (!disabled) { if (!disabled) {
baseStyles.push({ baseStyles.push({
color: light ? t.palette.primary_600 : t.palette.primary_500, color: t.palette.primary_600,
}) })
} else { } else {
baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) baseStyles.push({color: t.palette.primary_600, opacity: 0.5})

View File

@ -2,14 +2,14 @@ import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import { import {
useTheme,
atoms as a, atoms as a,
ViewStyleProp,
TextStyleProp,
flatten, flatten,
TextStyleProp,
useTheme,
ViewStyleProp,
} from '#/alf' } from '#/alf'
import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth'
import {Props} from '#/components/icons/common' import {Props} from '#/components/icons/common'
import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth'
export function IconCircle({ export function IconCircle({
icon: Icon, icon: Icon,
@ -32,8 +32,7 @@ export function IconCircle({
{ {
width: size === 'lg' ? 52 : 64, width: size === 'lg' ? 52 : 64,
height: size === 'lg' ? 52 : 64, height: size === 'lg' ? 52 : 64,
backgroundColor: backgroundColor: t.palette.primary_50,
t.name === 'light' ? t.palette.primary_50 : t.palette.primary_950,
}, },
flatten(style), flatten(style),
]}> ]}>

View File

@ -357,12 +357,11 @@ function TargetToggle({children}: React.PropsWithChildren<{}>) {
a.px_sm, a.px_sm,
gtMobile && a.px_md, gtMobile && a.px_md,
a.rounded_sm, a.rounded_sm,
t.atoms.bg_contrast_50, t.atoms.bg_contrast_25,
(ctx.hovered || ctx.focused) && t.atoms.bg_contrast_100, (ctx.hovered || ctx.focused) && t.atoms.bg_contrast_50,
ctx.selected && [ ctx.selected && [
{ {
backgroundColor: backgroundColor: t.palette.primary_50,
t.name === 'light' ? t.palette.primary_50 : t.palette.primary_975,
}, },
], ],
ctx.disabled && { ctx.disabled && {

View File

@ -196,10 +196,7 @@ function Selectable({
t.atoms.bg_contrast_50, t.atoms.bg_contrast_50,
(hovered || focused) && t.atoms.bg_contrast_100, (hovered || focused) && t.atoms.bg_contrast_100,
isSelected && { isSelected && {
backgroundColor: backgroundColor: t.palette.primary_100,
t.name === 'light'
? t.palette.primary_50
: t.palette.primary_975,
}, },
style, style,
]}> ]}>

View File

@ -72,8 +72,7 @@ let MessageItem = ({
lastInGroupRef.current = isLastInGroup lastInGroupRef.current = isLastInGroup
} }
const pendingColor = const pendingColor = t.palette.primary_200
t.name === 'light' ? t.palette.primary_200 : t.palette.primary_800
const rt = useMemo(() => { const rt = useMemo(() => {
return new RichTextAPI({text: message.text, facets: message.facets}) return new RichTextAPI({text: message.text, facets: message.facets})
@ -110,12 +109,7 @@ let MessageItem = ({
}> }>
<RichText <RichText
value={rt} value={rt}
style={[ style={[a.text_md, isFromSelf && {color: t.palette.white}]}
a.text_md,
isFromSelf && {color: t.palette.white},
isPending &&
t.name !== 'light' && {color: t.palette.primary_300},
]}
interactiveStyle={a.underline} interactiveStyle={a.underline}
enableTags enableTags
emojiMultiplier={3} emojiMultiplier={3}

View File

@ -101,16 +101,13 @@ export function useSharedInputStyles() {
] ]
const error: ViewStyle[] = [ const error: ViewStyle[] = [
{ {
backgroundColor: backgroundColor: t.palette.negative_25,
t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900, borderColor: t.palette.negative_300,
borderColor:
t.name === 'light' ? t.palette.negative_300 : t.palette.negative_800,
}, },
] ]
const errorHover: ViewStyle[] = [ const errorHover: ViewStyle[] = [
{ {
backgroundColor: backgroundColor: t.palette.negative_25,
t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900,
borderColor: t.palette.negative_500, borderColor: t.palette.negative_500,
}, },
] ]

View File

@ -281,24 +281,20 @@ export function createSharedToggleStyles({
if (selected) { if (selected) {
base.push({ base.push({
backgroundColor: backgroundColor: t.palette.primary_25,
t.name === 'light' ? t.palette.primary_25 : t.palette.primary_900,
borderColor: t.palette.primary_500, borderColor: t.palette.primary_500,
}) })
if (hovered) { if (hovered) {
baseHover.push({ baseHover.push({
backgroundColor: backgroundColor: t.palette.primary_100,
t.name === 'light' ? t.palette.primary_100 : t.palette.primary_800, borderColor: t.palette.primary_600,
borderColor:
t.name === 'light' ? t.palette.primary_600 : t.palette.primary_400,
}) })
} }
} else { } else {
if (hovered) { if (hovered) {
baseHover.push({ baseHover.push({
backgroundColor: backgroundColor: t.palette.contrast_50,
t.name === 'light' ? t.palette.contrast_50 : t.palette.contrast_100,
borderColor: t.palette.contrast_500, borderColor: t.palette.contrast_500,
}) })
} }
@ -306,16 +302,13 @@ export function createSharedToggleStyles({
if (isInvalid) { if (isInvalid) {
base.push({ base.push({
backgroundColor: backgroundColor: t.palette.negative_25,
t.name === 'light' ? t.palette.negative_25 : t.palette.negative_975, borderColor: t.palette.negative_300,
borderColor:
t.name === 'light' ? t.palette.negative_300 : t.palette.negative_800,
}) })
if (hovered) { if (hovered) {
baseHover.push({ baseHover.push({
backgroundColor: backgroundColor: t.palette.negative_25,
t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900,
borderColor: t.palette.negative_600, borderColor: t.palette.negative_600,
}) })
} }

View File

@ -4,7 +4,7 @@ import type {PathProps, SvgProps} from 'react-native-svg'
import {Defs, LinearGradient, Stop} from 'react-native-svg' import {Defs, LinearGradient, Stop} from 'react-native-svg'
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import {tokens} from '#/alf' import {tokens, useTheme} from '#/alf'
export type Props = { export type Props = {
fill?: PathProps['fill'] fill?: PathProps['fill']
@ -22,10 +22,11 @@ export const sizes = {
} }
export function useCommonSVGProps(props: Props) { export function useCommonSVGProps(props: Props) {
const t = useTheme()
const {fill, size, gradient, ...rest} = props const {fill, size, gradient, ...rest} = props
const style = StyleSheet.flatten(rest.style) const style = StyleSheet.flatten(rest.style)
const _size = Number(size ? sizes[size] : rest.width || sizes.md) const _size = Number(size ? sizes[size] : rest.width || sizes.md)
let _fill = fill || style?.color || tokens.color.blue_500 let _fill = fill || style?.color || t.palette.primary_500
let gradientDef = null let gradientDef = null
if (gradient && tokens.gradients[gradient]) { if (gradient && tokens.gradients[gradient]) {

View File

@ -1,7 +1,8 @@
import React, {ReactNode, createContext, useContext} from 'react' import React, {createContext, ReactNode, useContext} from 'react'
import {TextStyle, ViewStyle} from 'react-native' import {TextStyle, ViewStyle} from 'react-native'
import {ThemeName} from '#/alf/types'
import {darkTheme, defaultTheme, dimTheme} from './themes' import {darkTheme, defaultTheme, dimTheme} from './themes'
import {ThemeName} from '#/alf/themes'
export type ColorScheme = 'light' | 'dark' export type ColorScheme = 'light' | 'dark'

View File

@ -1,8 +1,8 @@
import {Platform} from 'react-native' import {Platform} from 'react-native'
import type {Theme} from './ThemeContext'
import {colors} from './styles'
import {darkPalette, lightPalette, dimPalette} from '#/alf/themes' import {darkPalette, dimPalette, lightPalette} from '#/alf/themes'
import {colors} from './styles'
import type {Theme} from './ThemeContext'
export const defaultTheme: Theme = { export const defaultTheme: Theme = {
colorScheme: 'light', colorScheme: 'light',
@ -308,8 +308,8 @@ export const darkTheme: Theme = {
textVeryLight: darkPalette.contrast_400, textVeryLight: darkPalette.contrast_400,
replyLine: darkPalette.contrast_200, replyLine: darkPalette.contrast_200,
replyLineDot: darkPalette.contrast_200, replyLineDot: darkPalette.contrast_200,
unreadNotifBg: darkPalette.primary_975, unreadNotifBg: darkPalette.primary_25,
unreadNotifBorder: darkPalette.primary_900, unreadNotifBorder: darkPalette.primary_100,
postCtrl: darkPalette.contrast_500, postCtrl: darkPalette.contrast_500,
brandText: darkPalette.primary_500, brandText: darkPalette.primary_500,
emptyStateIcon: darkPalette.contrast_300, emptyStateIcon: darkPalette.contrast_300,
@ -357,8 +357,8 @@ export const dimTheme: Theme = {
textVeryLight: dimPalette.contrast_400, textVeryLight: dimPalette.contrast_400,
replyLine: dimPalette.contrast_200, replyLine: dimPalette.contrast_200,
replyLineDot: dimPalette.contrast_200, replyLineDot: dimPalette.contrast_200,
unreadNotifBg: dimPalette.primary_975, unreadNotifBg: dimPalette.primary_25,
unreadNotifBorder: dimPalette.primary_900, unreadNotifBorder: dimPalette.primary_100,
postCtrl: dimPalette.contrast_500, postCtrl: dimPalette.contrast_500,
brandText: dimPalette.primary_500, brandText: dimPalette.primary_500,
emptyStateIcon: dimPalette.contrast_300, emptyStateIcon: dimPalette.contrast_300,

View File

@ -20,29 +20,31 @@ export function Buttons() {
<H1>Buttons</H1> <H1>Buttons</H1>
<View style={[a.flex_row, a.flex_wrap, a.gap_md, a.align_start]}> <View style={[a.flex_row, a.flex_wrap, a.gap_md, a.align_start]}>
{['primary', 'secondary', 'secondary_inverted'].map(color => ( {['primary', 'secondary', 'secondary_inverted', 'negative'].map(
<View key={color} style={[a.gap_md, a.align_start]}> color => (
{['solid', 'outline', 'ghost'].map(variant => ( <View key={color} style={[a.gap_md, a.align_start]}>
<React.Fragment key={variant}> {['solid', 'outline', 'ghost'].map(variant => (
<Button <React.Fragment key={variant}>
variant={variant as ButtonVariant} <Button
color={color as ButtonColor} variant={variant as ButtonVariant}
size="large" color={color as ButtonColor}
label="Click here"> size="large"
<ButtonText>Button</ButtonText> label="Click here">
</Button> <ButtonText>Button</ButtonText>
<Button </Button>
disabled <Button
variant={variant as ButtonVariant} disabled
color={color as ButtonColor} variant={variant as ButtonVariant}
size="large" color={color as ButtonColor}
label="Click here"> size="large"
<ButtonText>Button</ButtonText> label="Click here">
</Button> <ButtonText>Button</ButtonText>
</React.Fragment> </Button>
))} </React.Fragment>
</View> ))}
))} </View>
),
)}
<View style={[a.flex_row, a.gap_md, a.align_start]}> <View style={[a.flex_row, a.gap_md, a.align_start]}>
<View style={[a.gap_md, a.align_start]}> <View style={[a.gap_md, a.align_start]}>
@ -68,6 +70,7 @@ export function Buttons() {
), ),
)} )}
</View> </View>
{/*
<View style={[a.gap_md, a.align_start]}> <View style={[a.gap_md, a.align_start]}>
{['gradient_sunset', 'gradient_nordic', 'gradient_bonfire'].map( {['gradient_sunset', 'gradient_nordic', 'gradient_bonfire'].map(
name => ( name => (
@ -91,6 +94,7 @@ export function Buttons() {
), ),
)} )}
</View> </View>
*/}
</View> </View>
</View> </View>

View File

@ -41,6 +41,16 @@ export function Forms() {
/> />
</TextField.Root> </TextField.Root>
<TextField.Root>
<TextField.Icon icon={Globe} />
<TextField.Input
value={value}
onChangeText={setValue}
label="Text field"
isInvalid
/>
</TextField.Root>
<View style={[a.w_full]}> <View style={[a.w_full]}>
<TextField.LabelText>Text field</TextField.LabelText> <TextField.LabelText>Text field</TextField.LabelText>
<TextField.Root> <TextField.Root>