Smooth brain

This commit is contained in:
Eric Bailey
2024-01-04 12:09:04 -06:00
parent 85ebfc95d3
commit 1162d38f0f
6 changed files with 448 additions and 394 deletions
+423 -141
View File
@@ -1,133 +1,5 @@
import {TextStyle} from 'react-native'
import * as tokens from '#/alf/tokens'
const gap = Object.keys(tokens.space).reduce((acc, key) => {
const k = key as tokens.Space
acc[`gap_${k}`] = {
gap: tokens.space[k],
}
return acc
}, {} as Record<`gap_${tokens.Space}`, {gap: number}>)
const fontSize = Object.keys(tokens.fontSize).reduce((acc, key) => {
const k = key as tokens.FontSize
acc[`text_${k}`] = {
fontSize: tokens.fontSize[k],
lineHeight: tokens.fontSize[k],
}
return acc
}, {} as Record<`text_${tokens.FontSize}`, {fontSize: number; lineHeight: number}>)
const lineHeight = Object.keys(tokens.lineHeight).reduce((acc, key) => {
const k = key as tokens.LineHeight
acc[`leading_${k}`] = {
lineHeight: tokens.lineHeight[k],
}
return acc
}, {} as Record<`leading_${tokens.LineHeight}`, {lineHeight: number}>)
const fontWeight = Object.keys(tokens.fontWeight).reduce((acc, key) => {
const k = key as tokens.FontWeight
acc[`font_${k}`] = {
fontWeight: tokens.fontWeight[k] as TextStyle['fontWeight'],
}
return acc
}, {} as Record<`font_${tokens.FontWeight}`, {fontWeight: TextStyle['fontWeight']}>)
const radius = Object.keys(tokens.borderRadius).reduce((acc, key) => {
const k = key as tokens.BorderRadius
acc[`rounded_${k}`] = {
borderRadius: tokens.borderRadius[k],
}
return acc
}, {} as Record<`rounded_${tokens.BorderRadius}`, {borderRadius: number}>)
const padding = Object.keys(tokens.space).reduce(
(acc, key) => {
const k = key as tokens.Space
const value = tokens.space[k]
return {
...acc,
[`p_${k}`]: {
padding: value,
},
[`px_${k}`]: {
paddingLeft: value,
paddingRight: value,
},
[`py_${k}`]: {
paddingTop: value,
paddingBottom: value,
},
[`pt_${k}`]: {
paddingTop: value,
},
[`pb_${k}`]: {
paddingBottom: value,
},
[`pl_${k}`]: {
paddingLeft: value,
},
[`pr_${k}`]: {
paddingRight: value,
},
}
},
{} as Record<
`${'p' | 'px' | 'py' | 'pt' | 'pb' | 'pl' | 'pr'}_${tokens.Space}`,
{
padding?: number
paddingLeft?: number
paddingRight?: number
paddingTop?: number
paddingBottom?: number
}
>,
)
const margin = Object.keys(tokens.space).reduce(
(acc, key) => {
const k = key as tokens.Space
const value = tokens.space[k]
return {
...acc,
[`m_${k}`]: {
margin: value,
},
[`mx_${k}`]: {
margin: value,
marginRight: value,
},
[`my_${k}`]: {
marginTop: value,
marginBottom: value,
},
[`mt_${k}`]: {
marginTop: value,
},
[`mb_${k}`]: {
marginBottom: value,
},
[`ml_${k}`]: {
margin: value,
},
[`mr_${k}`]: {
marginRight: value,
},
}
},
{} as Record<
`${'m' | 'mx' | 'my' | 'mt' | 'mb' | 'ml' | 'mr'}_${tokens.Space}`,
{
margin?: number
marginLeft?: number
marginRight?: number
marginTop?: number
marginBottom?: number
}
>,
)
export const atoms = {
/*
* Positioning
@@ -160,18 +32,53 @@ export const atoms = {
zIndex: 50,
},
...radius,
/*
* Width
*/
w_full: {
width: '100%',
},
h_full: {
height: '100%',
},
/*
* Spacing
* Border radius
*/
...padding,
...margin,
rounded_sm: {
borderRadius: tokens.borderRadius.sm,
},
rounded_md: {
borderRadius: tokens.borderRadius.md,
},
rounded_full: {
borderRadius: tokens.borderRadius.full,
},
/*
* Flex
*/
...gap,
gap_xxs: {
gap: tokens.space.xxs,
},
gap_xs: {
gap: tokens.space.xs,
},
gap_sm: {
gap: tokens.space.sm,
},
gap_md: {
gap: tokens.space.md,
},
gap_lg: {
gap: tokens.space.lg,
},
gap_xl: {
gap: tokens.space.xl,
},
gap_xxl: {
gap: tokens.space.xxl,
},
flex: {
display: 'flex',
},
@@ -218,9 +125,64 @@ export const atoms = {
text_right: {
textAlign: 'right',
},
...fontSize,
...lineHeight,
...fontWeight,
text_xxs: {
fontSize: tokens.fontSize.xxs,
lineHeight: tokens.fontSize.xxs,
},
text_xs: {
fontSize: tokens.fontSize.xs,
lineHeight: tokens.fontSize.xs,
},
text_sm: {
fontSize: tokens.fontSize.sm,
lineHeight: tokens.fontSize.sm,
},
text_md: {
fontSize: tokens.fontSize.md,
lineHeight: tokens.fontSize.md,
},
text_lg: {
fontSize: tokens.fontSize.lg,
lineHeight: tokens.fontSize.lg,
},
text_xl: {
fontSize: tokens.fontSize.xl,
lineHeight: tokens.fontSize.xl,
},
text_xxl: {
fontSize: tokens.fontSize.xxl,
lineHeight: tokens.fontSize.xxl,
},
leading_xxs: {
lineHeight: tokens.fontSize.xxs,
},
leading_xs: {
lineHeight: tokens.fontSize.xs,
},
leading_sm: {
lineHeight: tokens.fontSize.sm,
},
leading_md: {
lineHeight: tokens.fontSize.md,
},
leading_lg: {
lineHeight: tokens.fontSize.lg,
},
leading_xl: {
lineHeight: tokens.fontSize.xl,
},
leading_xxl: {
lineHeight: tokens.fontSize.xxl,
},
font_normal: {
fontWeight: tokens.fontWeight.normal,
},
font_semibold: {
fontWeight: tokens.fontWeight.semibold,
},
font_bold: {
fontWeight: tokens.fontWeight.bold,
},
/*
* Border
@@ -236,12 +198,332 @@ export const atoms = {
},
/*
* Width
* Padding
*/
w_full: {
width: '100%',
p_xxs: {
padding: tokens.space.xxs,
},
h_full: {
height: '100%',
p_xs: {
padding: tokens.space.xs,
},
p_sm: {
padding: tokens.space.sm,
},
p_md: {
padding: tokens.space.md,
},
p_lg: {
padding: tokens.space.lg,
},
p_xl: {
padding: tokens.space.xl,
},
p_xxl: {
padding: tokens.space.xxl,
},
px_xxs: {
paddingLeft: tokens.space.xxs,
paddingRight: tokens.space.xxs,
},
px_xs: {
paddingLeft: tokens.space.xs,
paddingRight: tokens.space.xs,
},
px_sm: {
paddingLeft: tokens.space.sm,
paddingRight: tokens.space.sm,
},
px_md: {
paddingLeft: tokens.space.md,
paddingRight: tokens.space.md,
},
px_lg: {
paddingLeft: tokens.space.lg,
paddingRight: tokens.space.lg,
},
px_xl: {
paddingLeft: tokens.space.xl,
paddingRight: tokens.space.xl,
},
px_xxl: {
paddingLeft: tokens.space.xxl,
paddingRight: tokens.space.xxl,
},
py_xxs: {
paddingTop: tokens.space.xxs,
paddingBottom: tokens.space.xxs,
},
py_xs: {
paddingTop: tokens.space.xs,
paddingBottom: tokens.space.xs,
},
py_sm: {
paddingTop: tokens.space.sm,
paddingBottom: tokens.space.sm,
},
py_md: {
paddingTop: tokens.space.md,
paddingBottom: tokens.space.md,
},
py_lg: {
paddingTop: tokens.space.lg,
paddingBottom: tokens.space.lg,
},
py_xl: {
paddingTop: tokens.space.xl,
paddingBottom: tokens.space.xl,
},
py_xxl: {
paddingTop: tokens.space.xxl,
paddingBottom: tokens.space.xxl,
},
pt_xxs: {
paddingTop: tokens.space.xxs,
},
pt_xs: {
paddingTop: tokens.space.xs,
},
pt_sm: {
paddingTop: tokens.space.sm,
},
pt_md: {
paddingTop: tokens.space.md,
},
pt_lg: {
paddingTop: tokens.space.lg,
},
pt_xl: {
paddingTop: tokens.space.xl,
},
pt_xxl: {
paddingTop: tokens.space.xxl,
},
pb_xxs: {
paddingBottom: tokens.space.xxs,
},
pb_xs: {
paddingBottom: tokens.space.xs,
},
pb_sm: {
paddingBottom: tokens.space.sm,
},
pb_md: {
paddingBottom: tokens.space.md,
},
pb_lg: {
paddingBottom: tokens.space.lg,
},
pb_xl: {
paddingBottom: tokens.space.xl,
},
pb_xxl: {
paddingBottom: tokens.space.xxl,
},
pl_xxs: {
paddingLeft: tokens.space.xxs,
},
pl_xs: {
paddingLeft: tokens.space.xs,
},
pl_sm: {
paddingLeft: tokens.space.sm,
},
pl_md: {
paddingLeft: tokens.space.md,
},
pl_lg: {
paddingLeft: tokens.space.lg,
},
pl_xl: {
paddingLeft: tokens.space.xl,
},
pl_xxl: {
paddingLeft: tokens.space.xxl,
},
pr_xxs: {
paddingRight: tokens.space.xxs,
},
pr_xs: {
paddingRight: tokens.space.xs,
},
pr_sm: {
paddingRight: tokens.space.sm,
},
pr_md: {
paddingRight: tokens.space.md,
},
pr_lg: {
paddingRight: tokens.space.lg,
},
pr_xl: {
paddingRight: tokens.space.xl,
},
pr_xxl: {
paddingRight: tokens.space.xxl,
},
/*
* Margin
*/
m_xxs: {
margin: tokens.space.xxs,
},
m_xs: {
margin: tokens.space.xs,
},
m_sm: {
margin: tokens.space.sm,
},
m_md: {
margin: tokens.space.md,
},
m_lg: {
margin: tokens.space.lg,
},
m_xl: {
margin: tokens.space.xl,
},
m_xxl: {
margin: tokens.space.xxl,
},
mx_xxs: {
marginLeft: tokens.space.xxs,
marginRight: tokens.space.xxs,
},
mx_xs: {
marginLeft: tokens.space.xs,
marginRight: tokens.space.xs,
},
mx_sm: {
marginLeft: tokens.space.sm,
marginRight: tokens.space.sm,
},
mx_md: {
marginLeft: tokens.space.md,
marginRight: tokens.space.md,
},
mx_lg: {
marginLeft: tokens.space.lg,
marginRight: tokens.space.lg,
},
mx_xl: {
marginLeft: tokens.space.xl,
marginRight: tokens.space.xl,
},
mx_xxl: {
marginLeft: tokens.space.xxl,
marginRight: tokens.space.xxl,
},
my_xxs: {
marginTop: tokens.space.xxs,
marginBottom: tokens.space.xxs,
},
my_xs: {
marginTop: tokens.space.xs,
marginBottom: tokens.space.xs,
},
my_sm: {
marginTop: tokens.space.sm,
marginBottom: tokens.space.sm,
},
my_md: {
marginTop: tokens.space.md,
marginBottom: tokens.space.md,
},
my_lg: {
marginTop: tokens.space.lg,
marginBottom: tokens.space.lg,
},
my_xl: {
marginTop: tokens.space.xl,
marginBottom: tokens.space.xl,
},
my_xxl: {
marginTop: tokens.space.xxl,
marginBottom: tokens.space.xxl,
},
mt_xxs: {
marginTop: tokens.space.xxs,
},
mt_xs: {
marginTop: tokens.space.xs,
},
mt_sm: {
marginTop: tokens.space.sm,
},
mt_md: {
marginTop: tokens.space.md,
},
mt_lg: {
marginTop: tokens.space.lg,
},
mt_xl: {
marginTop: tokens.space.xl,
},
mt_xxl: {
marginTop: tokens.space.xxl,
},
mb_xxs: {
marginBottom: tokens.space.xxs,
},
mb_xs: {
marginBottom: tokens.space.xs,
},
mb_sm: {
marginBottom: tokens.space.sm,
},
mb_md: {
marginBottom: tokens.space.md,
},
mb_lg: {
marginBottom: tokens.space.lg,
},
mb_xl: {
marginBottom: tokens.space.xl,
},
mb_xxl: {
marginBottom: tokens.space.xxl,
},
ml_xxs: {
marginLeft: tokens.space.xxs,
},
ml_xs: {
marginLeft: tokens.space.xs,
},
ml_sm: {
marginLeft: tokens.space.sm,
},
ml_md: {
marginLeft: tokens.space.md,
},
ml_lg: {
marginLeft: tokens.space.lg,
},
ml_xl: {
marginLeft: tokens.space.xl,
},
ml_xxl: {
marginLeft: tokens.space.xxl,
},
mr_xxs: {
marginRight: tokens.space.xxs,
},
mr_xs: {
marginRight: tokens.space.xs,
},
mr_sm: {
marginRight: tokens.space.sm,
},
mr_md: {
marginRight: tokens.space.md,
},
mr_lg: {
marginRight: tokens.space.lg,
},
mr_xl: {
marginRight: tokens.space.xl,
},
mr_xxl: {
marginRight: tokens.space.xxl,
},
} as const
-171
View File
@@ -1,171 +0,0 @@
import React from 'react'
import {Pressable} from 'react-native'
import {useStyles} from '#/alf/system'
import {Text} from '#/alf/components/Typography'
export type ButtonType =
| 'primary'
| 'secondary'
| 'tertiary'
| 'positive'
| 'negative'
export type ButtonSize = 'small' | 'large'
export type ButtonProps = React.ComponentProps<typeof Pressable> & {
type?: ButtonType
size?: ButtonSize
}
const colorVariants: {
[key in ButtonType]: Parameters<typeof useStyles>[0]
} = {
primary: {
pressable: {
bg: 'primary',
},
pressableHovered: {
bg: 'primary',
},
pressableFocused: {
bg: 'primary',
},
pressablePressed: {
bg: 'primary',
},
text: {
color: 'white',
},
},
secondary: {
pressable: {
bg: 'l2',
},
pressableHovered: {
bg: 'l1',
},
pressableFocused: {
bg: 'l1',
},
pressablePressed: {
bg: 'l1',
},
text: {
color: 'l6',
},
},
tertiary: {},
positive: {
pressable: {
bg: 'green',
},
pressableHovered: {
bg: 'green',
},
pressableFocused: {
bg: 'green',
},
pressablePressed: {
bg: 'green',
},
text: {
color: 'l0',
},
},
negative: {
pressable: {
bg: 'red',
},
pressableHovered: {
bg: 'red',
},
pressableFocused: {
bg: 'red',
},
pressablePressed: {
bg: 'red',
},
text: {
color: 'l0',
},
},
}
const sizeVariants: {
[key in ButtonSize]: Parameters<typeof useStyles>[0]
} = {
small: {
pressable: {
py: 'm',
px: 'l',
radius: 'm',
},
text: {
fontSize: 'm',
fontWeight: 'semi',
},
},
large: {
pressable: {
py: 'm',
px: 'l',
radius: 'm',
},
text: {
fontSize: 'm',
fontWeight: 'semi',
},
},
}
export function Button({
children,
type = 'primary',
size = 'large',
...rest
}: React.PropsWithChildren<ButtonProps>) {
const styles = useStyles(
React.useMemo<Parameters<typeof useStyles>[0]>(
() => ({
pressable: {
...colorVariants[type].pressable,
...sizeVariants[size].pressable,
},
text: {
textAlign: 'center',
...colorVariants[type].text,
...sizeVariants[size].text,
},
pressableHovered: {
...colorVariants[type].pressableHovered,
},
pressableFocused: {
...colorVariants[type].pressableFocused,
},
pressablePressed: {
...colorVariants[type].pressablePressed,
},
}),
[type, size],
),
)
return (
<Pressable
{...rest}
style={state =>
[
styles.pressable,
state.hovered && styles.pressableHovered,
state.focused && styles.pressableFocused,
state.pressed && styles.pressablePressed,
].filter(Boolean)
}>
{typeof children === 'string' ? (
<Text style={[styles.text]}>{children}</Text>
) : (
children
)}
</Pressable>
)
}
-74
View File
@@ -1,74 +0,0 @@
import {Text as RNText} from 'react-native'
import {styled} from '#/alf/system'
import {web} from '#/alf/util/platform'
export const Text = styled(RNText, {
color: 'l7',
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: 'l7',
fontSize: 'l',
gtMobile: {
fontSize: 'xl',
},
...web({
role: 'heading',
'aria-level': 1,
}),
})
export const H2 = styled(RNText, {
color: 'l7',
fontSize: 'm',
gtMobile: {
fontSize: 'l',
},
...web({
role: 'heading',
'aria-level': 2,
}),
})
export const H3 = styled(RNText, {
color: 'l7',
fontSize: 'm',
...web({
role: 'heading',
'aria-level': 3,
}),
})
export const H4 = styled(RNText, {
color: 'l7',
fontSize: 's',
...web({
role: 'heading',
'aria-level': 4,
}),
})
export const H5 = styled(RNText, {
color: 'l7',
fontSize: 'xs',
...web({
role: 'heading',
'aria-level': 5,
}),
})
export const H6 = styled(RNText, {
color: 'l7',
fontSize: 'xxs',
...web({
role: 'heading',
'aria-level': 6,
}),
})
export const P = styled(RNText, {
color: 'l7',
fontSize: 's',
...web({
role: 'paragraph',
}),
})
+3 -1
View File
@@ -1,7 +1,9 @@
import * as tokens from '#/alf/tokens'
import type {Mutable} from '#/alf/types'
export type ThemeName = 'light' | 'dark'
export type Theme = typeof light
export type ReadonlyTheme = typeof light
export type Theme = Mutable<ReadonlyTheme>
export type Palette = {
primary: string
+6 -7
View File
@@ -51,7 +51,7 @@ export const color = {
red_800: `hsl(349, 96%, 25%)`,
red_900: `hsl(349, 96%, 15%)`,
red_1000: `hsl(349, 96%, 5%)`,
}
} as const
export const space = {
xxs: 2,
@@ -61,7 +61,7 @@ export const space = {
lg: 18,
xl: 24,
xxl: 32,
}
} as const
export const fontSize = {
xxs: 10,
@@ -71,27 +71,26 @@ export const fontSize = {
lg: 18,
xl: 22,
xxl: 26,
}
} as const
// TODO test
export const lineHeight = {
none: 1,
normal: 1.5,
relaxed: 1.625,
}
} as const
export const borderRadius = {
sm: 8,
md: 12,
xl: 36,
full: 999,
}
} as const
export const fontWeight = {
normal: '400',
semibold: '600',
bold: '900',
}
} as const
export type Color = keyof typeof color
export type Space = keyof typeof space
+16
View File
@@ -0,0 +1,16 @@
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]>
}