Application Layout Framework (#1732)
* Initial library setup * Add docblocks * Some cleanup * New storybook * Playing around * Remove silly test, use for...in * Memo * Memo * Add hooks example * Tweak colors, bit of cleanup * Improve macro handling * Add some more examples * Rename for better diff * Cleanup * Add nested context example * Add todo * Less break more perf * Buttons, you get the idea * Fix test * Remove temp colors * Add a few more common macros * Docs * Perf improvements * Alf go brrrr * Update breakpoint handling * I think it'll work * Better naming, better code * Fix typo * Some renaming * More complete pass at Tailwind naming * Build out storybook * Playing around with curves * Revert "Playing around with curves" This reverts commit 6b0e0e5c9d842a2d9af31b53affe2f6291c3fa0d. * Smooth brain * Remove outdated docs * Some docs, fix line-height values, export tokenszio/stable
parent
0ee0554b86
commit
a5b474895a
|
@ -7,6 +7,7 @@ import {RootSiblingParent} from 'react-native-root-siblings'
|
||||||
|
|
||||||
import 'view/icons'
|
import 'view/icons'
|
||||||
|
|
||||||
|
import {ThemeProvider as Alf} from '#/alf'
|
||||||
import {init as initPersistedState} from '#/state/persisted'
|
import {init as initPersistedState} from '#/state/persisted'
|
||||||
import {useColorMode} from 'state/shell'
|
import {useColorMode} from 'state/shell'
|
||||||
import {Shell} from 'view/shell/index'
|
import {Shell} from 'view/shell/index'
|
||||||
|
@ -28,11 +29,13 @@ import {
|
||||||
} from 'state/session'
|
} from 'state/session'
|
||||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
|
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const {isInitialLoad, currentAccount} = useSession()
|
const {isInitialLoad, currentAccount} = useSession()
|
||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
const theme = useColorModeTheme(colorMode)
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -44,23 +47,25 @@ function InnerApp() {
|
||||||
if (isInitialLoad) return null
|
if (isInitialLoad) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment
|
<Alf theme={theme}>
|
||||||
// Resets the entire tree below when it changes:
|
<React.Fragment
|
||||||
key={currentAccount?.did}>
|
// Resets the entire tree below when it changes:
|
||||||
<LoggedOutViewProvider>
|
key={currentAccount?.did}>
|
||||||
<UnreadNotifsProvider>
|
<LoggedOutViewProvider>
|
||||||
<ThemeProvider theme={colorMode}>
|
<UnreadNotifsProvider>
|
||||||
{/* All components should be within this provider */}
|
<ThemeProvider theme={colorMode}>
|
||||||
<RootSiblingParent>
|
{/* All components should be within this provider */}
|
||||||
<SafeAreaProvider>
|
<RootSiblingParent>
|
||||||
<Shell />
|
<SafeAreaProvider>
|
||||||
</SafeAreaProvider>
|
<Shell />
|
||||||
</RootSiblingParent>
|
</SafeAreaProvider>
|
||||||
<ToastContainer />
|
</RootSiblingParent>
|
||||||
</ThemeProvider>
|
<ToastContainer />
|
||||||
</UnreadNotifsProvider>
|
</ThemeProvider>
|
||||||
</LoggedOutViewProvider>
|
</UnreadNotifsProvider>
|
||||||
</React.Fragment>
|
</LoggedOutViewProvider>
|
||||||
|
</React.Fragment>
|
||||||
|
</Alf>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ import {ProfileListScreen} from './view/screens/ProfileList'
|
||||||
import {PostThreadScreen} from './view/screens/PostThread'
|
import {PostThreadScreen} from './view/screens/PostThread'
|
||||||
import {PostLikedByScreen} from './view/screens/PostLikedBy'
|
import {PostLikedByScreen} from './view/screens/PostLikedBy'
|
||||||
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
|
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
|
||||||
import {DebugScreen} from './view/screens/Debug'
|
import {DebugScreen} from './view/screens/DebugNew'
|
||||||
import {LogScreen} from './view/screens/Log'
|
import {LogScreen} from './view/screens/Log'
|
||||||
import {SupportScreen} from './view/screens/Support'
|
import {SupportScreen} from './view/screens/Support'
|
||||||
import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
|
import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Application Layout Framework (ALF)
|
||||||
|
|
||||||
|
A set of UI primitives and components.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Naming conventions follow Tailwind — delimited with a `_` instead of `-` to
|
||||||
|
enable object access — with a couple exceptions:
|
||||||
|
|
||||||
|
**Spacing**
|
||||||
|
|
||||||
|
Uses "t-shirt" sizes `xxs`, `xs`, `sm`, `md`, `lg`, `xl` and `xxl` instead of
|
||||||
|
increments of 4px. We only use a few common spacings, and otherwise typically
|
||||||
|
rely on many one-off values.
|
||||||
|
|
||||||
|
**Text Size**
|
||||||
|
|
||||||
|
Uses "t-shirt" sizes `xxs`, `xs`, `sm`, `md`, `lg`, `xl` and `xxl` to match our
|
||||||
|
type scale.
|
||||||
|
|
||||||
|
**Line Height**
|
||||||
|
|
||||||
|
The text size atoms also apply a line-height with the same value as the size,
|
||||||
|
for a 1:1 ratio. `tight` and `normal` are retained for use in the few places
|
||||||
|
where we need leading.
|
||||||
|
|
||||||
|
### Atoms
|
||||||
|
|
||||||
|
An (mostly-complete) set of style definitions that match Tailwind CSS selectors.
|
||||||
|
These are static and reused throughout the app.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { atoms } from '#/alf'
|
||||||
|
|
||||||
|
<View style={[atoms.flex_row]} />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
Any values that rely on the theme, namely colors.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
<View style={[atoms.flex_row, t.atoms.bg]} />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Breakpoints
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const b = useBreakpoints()
|
||||||
|
|
||||||
|
if (b.gtMobile) {
|
||||||
|
// render tablet or desktop UI
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,514 @@
|
||||||
|
import * as tokens from '#/alf/tokens'
|
||||||
|
|
||||||
|
export const atoms = {
|
||||||
|
/*
|
||||||
|
* Positioning
|
||||||
|
*/
|
||||||
|
absolute: {
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
relative: {
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
inset_0: {
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
|
z_10: {
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
z_20: {
|
||||||
|
zIndex: 20,
|
||||||
|
},
|
||||||
|
z_30: {
|
||||||
|
zIndex: 30,
|
||||||
|
},
|
||||||
|
z_40: {
|
||||||
|
zIndex: 40,
|
||||||
|
},
|
||||||
|
z_50: {
|
||||||
|
zIndex: 50,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Width
|
||||||
|
*/
|
||||||
|
w_full: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
h_full: {
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Border radius
|
||||||
|
*/
|
||||||
|
rounded_sm: {
|
||||||
|
borderRadius: tokens.borderRadius.sm,
|
||||||
|
},
|
||||||
|
rounded_md: {
|
||||||
|
borderRadius: tokens.borderRadius.md,
|
||||||
|
},
|
||||||
|
rounded_full: {
|
||||||
|
borderRadius: tokens.borderRadius.full,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flex
|
||||||
|
*/
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
flex_row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
flex_wrap: {
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
flex_1: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
flex_grow: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
flex_shrink: {
|
||||||
|
flexShrink: 1,
|
||||||
|
},
|
||||||
|
justify_center: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
justify_between: {
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
justify_end: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
align_center: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
align_start: {
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
},
|
||||||
|
align_end: {
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Text
|
||||||
|
*/
|
||||||
|
text_center: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
text_right: {
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
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_tight: {
|
||||||
|
lineHeight: 1.25,
|
||||||
|
},
|
||||||
|
leading_normal: {
|
||||||
|
lineHeight: 1.5,
|
||||||
|
},
|
||||||
|
font_normal: {
|
||||||
|
fontWeight: tokens.fontWeight.normal,
|
||||||
|
},
|
||||||
|
font_semibold: {
|
||||||
|
fontWeight: tokens.fontWeight.semibold,
|
||||||
|
},
|
||||||
|
font_bold: {
|
||||||
|
fontWeight: tokens.fontWeight.bold,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Border
|
||||||
|
*/
|
||||||
|
border: {
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
border_t: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
},
|
||||||
|
border_b: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Padding
|
||||||
|
*/
|
||||||
|
p_xxs: {
|
||||||
|
padding: tokens.space.xxs,
|
||||||
|
},
|
||||||
|
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
|
|
@ -0,0 +1,92 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {Dimensions} from 'react-native'
|
||||||
|
import * as themes from '#/alf/themes'
|
||||||
|
|
||||||
|
export * as tokens from '#/alf/tokens'
|
||||||
|
export {atoms} from '#/alf/atoms'
|
||||||
|
export * from '#/alf/util/platform'
|
||||||
|
|
||||||
|
type BreakpointName = keyof typeof breakpoints
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Breakpoints
|
||||||
|
*/
|
||||||
|
const breakpoints: {
|
||||||
|
[key: string]: number
|
||||||
|
} = {
|
||||||
|
gtMobile: 800,
|
||||||
|
gtTablet: 1200,
|
||||||
|
}
|
||||||
|
function getActiveBreakpoints({width}: {width: number}) {
|
||||||
|
const active: (keyof typeof breakpoints)[] = Object.keys(breakpoints).filter(
|
||||||
|
breakpoint => width >= breakpoints[breakpoint],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: active[active.length - 1],
|
||||||
|
gtMobile: active.includes('gtMobile'),
|
||||||
|
gtTablet: active.includes('gtTablet'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Context
|
||||||
|
*/
|
||||||
|
export const Context = React.createContext<{
|
||||||
|
themeName: themes.ThemeName
|
||||||
|
theme: themes.Theme
|
||||||
|
breakpoints: {
|
||||||
|
active: BreakpointName | undefined
|
||||||
|
gtMobile: boolean
|
||||||
|
gtTablet: boolean
|
||||||
|
}
|
||||||
|
}>({
|
||||||
|
themeName: 'light',
|
||||||
|
theme: themes.light,
|
||||||
|
breakpoints: {
|
||||||
|
active: undefined,
|
||||||
|
gtMobile: false,
|
||||||
|
gtTablet: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export function ThemeProvider({
|
||||||
|
children,
|
||||||
|
theme: themeName,
|
||||||
|
}: React.PropsWithChildren<{theme: themes.ThemeName}>) {
|
||||||
|
const theme = themes[themeName]
|
||||||
|
const [breakpoints, setBreakpoints] = React.useState(() =>
|
||||||
|
getActiveBreakpoints({width: Dimensions.get('window').width}),
|
||||||
|
)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const listener = Dimensions.addEventListener('change', ({window}) => {
|
||||||
|
const bp = getActiveBreakpoints({width: window.width})
|
||||||
|
if (bp.active !== breakpoints.active) setBreakpoints(bp)
|
||||||
|
})
|
||||||
|
|
||||||
|
return listener.remove
|
||||||
|
}, [breakpoints, setBreakpoints])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider
|
||||||
|
value={React.useMemo(
|
||||||
|
() => ({
|
||||||
|
themeName: themeName,
|
||||||
|
theme: theme,
|
||||||
|
breakpoints,
|
||||||
|
}),
|
||||||
|
[theme, themeName, breakpoints],
|
||||||
|
)}>
|
||||||
|
{children}
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTheme() {
|
||||||
|
return React.useContext(Context).theme
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBreakpoints() {
|
||||||
|
return React.useContext(Context).breakpoints
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
import * as tokens from '#/alf/tokens'
|
||||||
|
import type {Mutable} from '#/alf/types'
|
||||||
|
|
||||||
|
export type ThemeName = 'light' | 'dark'
|
||||||
|
export type ReadonlyTheme = typeof light
|
||||||
|
export type Theme = Mutable<ReadonlyTheme>
|
||||||
|
|
||||||
|
export type Palette = {
|
||||||
|
primary: string
|
||||||
|
positive: string
|
||||||
|
negative: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const lightPalette: Palette = {
|
||||||
|
primary: tokens.color.blue_500,
|
||||||
|
positive: tokens.color.green_500,
|
||||||
|
negative: tokens.color.red_500,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const darkPalette: Palette = {
|
||||||
|
primary: tokens.color.blue_500,
|
||||||
|
positive: tokens.color.green_400,
|
||||||
|
negative: tokens.color.red_400,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const light = {
|
||||||
|
palette: lightPalette,
|
||||||
|
atoms: {
|
||||||
|
text: {
|
||||||
|
color: tokens.color.gray_1000,
|
||||||
|
},
|
||||||
|
text_contrast_700: {
|
||||||
|
color: tokens.color.gray_700,
|
||||||
|
},
|
||||||
|
text_contrast_500: {
|
||||||
|
color: tokens.color.gray_500,
|
||||||
|
},
|
||||||
|
text_inverted: {
|
||||||
|
color: tokens.color.white,
|
||||||
|
},
|
||||||
|
bg: {
|
||||||
|
backgroundColor: tokens.color.white,
|
||||||
|
},
|
||||||
|
bg_contrast_100: {
|
||||||
|
backgroundColor: tokens.color.gray_100,
|
||||||
|
},
|
||||||
|
bg_contrast_200: {
|
||||||
|
backgroundColor: tokens.color.gray_200,
|
||||||
|
},
|
||||||
|
bg_contrast_300: {
|
||||||
|
backgroundColor: tokens.color.gray_300,
|
||||||
|
},
|
||||||
|
bg_positive: {
|
||||||
|
backgroundColor: tokens.color.green_500,
|
||||||
|
},
|
||||||
|
bg_negative: {
|
||||||
|
backgroundColor: tokens.color.red_400,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
borderColor: tokens.color.gray_200,
|
||||||
|
},
|
||||||
|
border_contrast_500: {
|
||||||
|
borderColor: tokens.color.gray_500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dark: Theme = {
|
||||||
|
palette: darkPalette,
|
||||||
|
atoms: {
|
||||||
|
text: {
|
||||||
|
color: tokens.color.white,
|
||||||
|
},
|
||||||
|
text_contrast_700: {
|
||||||
|
color: tokens.color.gray_300,
|
||||||
|
},
|
||||||
|
text_contrast_500: {
|
||||||
|
color: tokens.color.gray_500,
|
||||||
|
},
|
||||||
|
text_inverted: {
|
||||||
|
color: tokens.color.gray_1000,
|
||||||
|
},
|
||||||
|
bg: {
|
||||||
|
backgroundColor: tokens.color.gray_1000,
|
||||||
|
},
|
||||||
|
bg_contrast_100: {
|
||||||
|
backgroundColor: tokens.color.gray_900,
|
||||||
|
},
|
||||||
|
bg_contrast_200: {
|
||||||
|
backgroundColor: tokens.color.gray_800,
|
||||||
|
},
|
||||||
|
bg_contrast_300: {
|
||||||
|
backgroundColor: tokens.color.gray_700,
|
||||||
|
},
|
||||||
|
bg_positive: {
|
||||||
|
backgroundColor: tokens.color.green_400,
|
||||||
|
},
|
||||||
|
bg_negative: {
|
||||||
|
backgroundColor: tokens.color.red_400,
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
borderColor: tokens.color.gray_800,
|
||||||
|
},
|
||||||
|
border_contrast_500: {
|
||||||
|
borderColor: tokens.color.gray_500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
const BLUE_HUE = 211
|
||||||
|
const GRAYSCALE_SATURATION = 22
|
||||||
|
|
||||||
|
export const color = {
|
||||||
|
white: '#FFFFFF',
|
||||||
|
|
||||||
|
gray_0: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 100%)`,
|
||||||
|
gray_100: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 95%)`,
|
||||||
|
gray_200: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 85%)`,
|
||||||
|
gray_300: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 75%)`,
|
||||||
|
gray_400: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 65%)`,
|
||||||
|
gray_500: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 55%)`,
|
||||||
|
gray_600: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 45%)`,
|
||||||
|
gray_700: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 35%)`,
|
||||||
|
gray_800: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 25%)`,
|
||||||
|
gray_900: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 15%)`,
|
||||||
|
gray_1000: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 5%)`,
|
||||||
|
|
||||||
|
blue_0: `hsl(${BLUE_HUE}, 99%, 100%)`,
|
||||||
|
blue_100: `hsl(${BLUE_HUE}, 99%, 93%)`,
|
||||||
|
blue_200: `hsl(${BLUE_HUE}, 99%, 83%)`,
|
||||||
|
blue_300: `hsl(${BLUE_HUE}, 99%, 73%)`,
|
||||||
|
blue_400: `hsl(${BLUE_HUE}, 99%, 63%)`,
|
||||||
|
blue_500: `hsl(${BLUE_HUE}, 99%, 53%)`,
|
||||||
|
blue_600: `hsl(${BLUE_HUE}, 99%, 43%)`,
|
||||||
|
blue_700: `hsl(${BLUE_HUE}, 99%, 33%)`,
|
||||||
|
blue_800: `hsl(${BLUE_HUE}, 99%, 23%)`,
|
||||||
|
blue_900: `hsl(${BLUE_HUE}, 99%, 13%)`,
|
||||||
|
blue_1000: `hsl(${BLUE_HUE}, 99%, 8%)`,
|
||||||
|
|
||||||
|
green_0: `hsl(130, 60%, 100%)`,
|
||||||
|
green_100: `hsl(130, 60%, 95%)`,
|
||||||
|
green_200: `hsl(130, 60%, 85%)`,
|
||||||
|
green_300: `hsl(130, 60%, 75%)`,
|
||||||
|
green_400: `hsl(130, 60%, 65%)`,
|
||||||
|
green_500: `hsl(130, 60%, 55%)`,
|
||||||
|
green_600: `hsl(130, 60%, 45%)`,
|
||||||
|
green_700: `hsl(130, 60%, 35%)`,
|
||||||
|
green_800: `hsl(130, 60%, 25%)`,
|
||||||
|
green_900: `hsl(130, 60%, 15%)`,
|
||||||
|
green_1000: `hsl(130, 60%, 5%)`,
|
||||||
|
|
||||||
|
red_0: `hsl(349, 96%, 100%)`,
|
||||||
|
red_100: `hsl(349, 96%, 95%)`,
|
||||||
|
red_200: `hsl(349, 96%, 85%)`,
|
||||||
|
red_300: `hsl(349, 96%, 75%)`,
|
||||||
|
red_400: `hsl(349, 96%, 65%)`,
|
||||||
|
red_500: `hsl(349, 96%, 55%)`,
|
||||||
|
red_600: `hsl(349, 96%, 45%)`,
|
||||||
|
red_700: `hsl(349, 96%, 35%)`,
|
||||||
|
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,
|
||||||
|
xs: 4,
|
||||||
|
sm: 8,
|
||||||
|
md: 12,
|
||||||
|
lg: 18,
|
||||||
|
xl: 24,
|
||||||
|
xxl: 32,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const fontSize = {
|
||||||
|
xxs: 10,
|
||||||
|
xs: 12,
|
||||||
|
sm: 14,
|
||||||
|
md: 16,
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
export type FontSize = keyof typeof fontSize
|
||||||
|
export type LineHeight = keyof typeof lineHeight
|
||||||
|
export type BorderRadius = keyof typeof borderRadius
|
||||||
|
export type FontWeight = keyof typeof fontWeight
|
|
@ -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]>
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {Platform} from 'react-native'
|
||||||
|
|
||||||
|
export function web(value: any) {
|
||||||
|
return Platform.select({
|
||||||
|
web: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ios(value: any) {
|
||||||
|
return Platform.select({
|
||||||
|
ios: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function android(value: any) {
|
||||||
|
return Platform.select({
|
||||||
|
android: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function native(value: any) {
|
||||||
|
return Platform.select({
|
||||||
|
native: value,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {useColorScheme} from 'react-native'
|
||||||
|
|
||||||
|
import * as persisted from '#/state/persisted'
|
||||||
|
|
||||||
|
export function useColorModeTheme(
|
||||||
|
theme: persisted.Schema['colorMode'],
|
||||||
|
): 'light' | 'dark' {
|
||||||
|
const colorScheme = useColorScheme()
|
||||||
|
return (theme === 'system' ? colorScheme : theme) || 'light'
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {Pressable, Text, PressableProps, TextProps} from 'react-native'
|
||||||
|
import * as tokens from '#/alf/tokens'
|
||||||
|
import {atoms} from '#/alf'
|
||||||
|
|
||||||
|
export type ButtonType =
|
||||||
|
| 'primary'
|
||||||
|
| 'secondary'
|
||||||
|
| 'tertiary'
|
||||||
|
| 'positive'
|
||||||
|
| 'negative'
|
||||||
|
export type ButtonSize = 'small' | 'large'
|
||||||
|
|
||||||
|
export type VariantProps = {
|
||||||
|
type?: ButtonType
|
||||||
|
size?: ButtonSize
|
||||||
|
}
|
||||||
|
type ButtonState = {
|
||||||
|
pressed: boolean
|
||||||
|
hovered: boolean
|
||||||
|
focused: boolean
|
||||||
|
}
|
||||||
|
export type ButtonProps = Omit<PressableProps, 'children'> &
|
||||||
|
VariantProps & {
|
||||||
|
children:
|
||||||
|
| ((props: {
|
||||||
|
state: ButtonState
|
||||||
|
type?: ButtonType
|
||||||
|
size?: ButtonSize
|
||||||
|
}) => React.ReactNode)
|
||||||
|
| React.ReactNode
|
||||||
|
| string
|
||||||
|
}
|
||||||
|
export type ButtonTextProps = TextProps & VariantProps
|
||||||
|
|
||||||
|
export function Button({children, style, type, size, ...rest}: ButtonProps) {
|
||||||
|
const {baseStyles, hoverStyles} = React.useMemo(() => {
|
||||||
|
const baseStyles = []
|
||||||
|
const hoverStyles = []
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'primary':
|
||||||
|
baseStyles.push({
|
||||||
|
backgroundColor: tokens.color.blue_500,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'secondary':
|
||||||
|
baseStyles.push({
|
||||||
|
backgroundColor: tokens.color.gray_200,
|
||||||
|
})
|
||||||
|
hoverStyles.push({
|
||||||
|
backgroundColor: tokens.color.gray_100,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 'large':
|
||||||
|
baseStyles.push(
|
||||||
|
atoms.py_md,
|
||||||
|
atoms.px_xl,
|
||||||
|
atoms.rounded_md,
|
||||||
|
atoms.gap_sm,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case 'small':
|
||||||
|
baseStyles.push(
|
||||||
|
atoms.py_sm,
|
||||||
|
atoms.px_md,
|
||||||
|
atoms.rounded_sm,
|
||||||
|
atoms.gap_xs,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
baseStyles,
|
||||||
|
hoverStyles,
|
||||||
|
}
|
||||||
|
}, [type, size])
|
||||||
|
|
||||||
|
const [state, setState] = React.useState({
|
||||||
|
pressed: false,
|
||||||
|
hovered: false,
|
||||||
|
focused: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onPressIn = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
pressed: true,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
const onPressOut = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
pressed: false,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
const onHoverIn = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
hovered: true,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
const onHoverOut = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
hovered: false,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
const onFocus = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
focused: true,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
const onBlur = React.useCallback(() => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
focused: false,
|
||||||
|
}))
|
||||||
|
}, [setState])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
{...rest}
|
||||||
|
style={state => [
|
||||||
|
atoms.flex_row,
|
||||||
|
atoms.align_center,
|
||||||
|
...baseStyles,
|
||||||
|
...(state.hovered ? hoverStyles : []),
|
||||||
|
typeof style === 'function' ? style(state) : style,
|
||||||
|
]}
|
||||||
|
onPressIn={onPressIn}
|
||||||
|
onPressOut={onPressOut}
|
||||||
|
onHoverIn={onHoverIn}
|
||||||
|
onHoverOut={onHoverOut}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}>
|
||||||
|
{typeof children === 'string' ? (
|
||||||
|
<ButtonText type={type} size={size}>
|
||||||
|
{children}
|
||||||
|
</ButtonText>
|
||||||
|
) : typeof children === 'function' ? (
|
||||||
|
children({state, type, size})
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ButtonText({
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
type,
|
||||||
|
size,
|
||||||
|
...rest
|
||||||
|
}: ButtonTextProps) {
|
||||||
|
const textStyles = React.useMemo(() => {
|
||||||
|
const base = []
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'primary':
|
||||||
|
base.push({color: tokens.color.white})
|
||||||
|
break
|
||||||
|
case 'secondary':
|
||||||
|
base.push({
|
||||||
|
color: tokens.color.gray_700,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 'small':
|
||||||
|
base.push(atoms.text_sm, {paddingBottom: 1})
|
||||||
|
break
|
||||||
|
case 'large':
|
||||||
|
base.push(atoms.text_md, {paddingBottom: 1})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return base
|
||||||
|
}, [type, size])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
{...rest}
|
||||||
|
style={[
|
||||||
|
atoms.flex_1,
|
||||||
|
atoms.font_semibold,
|
||||||
|
atoms.text_center,
|
||||||
|
...textStyles,
|
||||||
|
style,
|
||||||
|
]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {Text as RNText, TextProps} from 'react-native'
|
||||||
|
import {useTheme, atoms, web} from '#/alf'
|
||||||
|
|
||||||
|
export function Text({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
return <RNText style={[atoms.text_sm, t.atoms.text, style]} {...rest} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H1({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 1,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_xl, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H2({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 2,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_lg, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H3({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 3,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_md, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H4({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 4,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_sm, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H5({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 5,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_xs, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function H6({style, ...rest}: TextProps) {
|
||||||
|
const t = useTheme()
|
||||||
|
const attr =
|
||||||
|
web({
|
||||||
|
role: 'heading',
|
||||||
|
'aria-level': 6,
|
||||||
|
}) || {}
|
||||||
|
return (
|
||||||
|
<RNText
|
||||||
|
{...attr}
|
||||||
|
{...rest}
|
||||||
|
style={[atoms.text_xxs, atoms.font_bold, t.atoms.text, style]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,541 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
|
||||||
|
import {useSetColorMode} from '#/state/shell'
|
||||||
|
import * as tokens from '#/alf/tokens'
|
||||||
|
import {atoms as a, useTheme, useBreakpoints, ThemeProvider as Alf} from '#/alf'
|
||||||
|
import {Button, ButtonText} from '#/view/com/Button'
|
||||||
|
import {Text, H1, H2, H3, H4, H5, H6} from '#/view/com/Typography'
|
||||||
|
|
||||||
|
function ThemeSelector() {
|
||||||
|
const setColorMode = useSetColorMode()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
size="small"
|
||||||
|
onPress={() => setColorMode('system')}>
|
||||||
|
System
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
size="small"
|
||||||
|
onPress={() => setColorMode('light')}>
|
||||||
|
Light
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
size="small"
|
||||||
|
onPress={() => setColorMode('dark')}>
|
||||||
|
Dark
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreakpointDebugger() {
|
||||||
|
const t = useTheme()
|
||||||
|
const breakpoints = useBreakpoints()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<H3 style={[a.pb_md]}>Breakpoint Debugger</H3>
|
||||||
|
<Text style={[a.pb_md]}>
|
||||||
|
Current breakpoint: {!breakpoints.gtMobile && <Text>mobile</Text>}
|
||||||
|
{breakpoints.gtMobile && !breakpoints.gtTablet && <Text>tablet</Text>}
|
||||||
|
{breakpoints.gtTablet && <Text>desktop</Text>}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[a.p_md, t.atoms.bg_contrast_100, {fontFamily: 'monospace'}]}>
|
||||||
|
{JSON.stringify(breakpoints, null, 2)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ThemedSection() {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[t.atoms.bg, a.gap_md, a.p_xl]}>
|
||||||
|
<H3 style={[a.font_bold]}>theme.atoms.text</H3>
|
||||||
|
<View style={[a.flex_1, t.atoms.border, a.border_t]} />
|
||||||
|
<H3 style={[a.font_bold, t.atoms.text_contrast_700]}>
|
||||||
|
theme.atoms.text_contrast_700
|
||||||
|
</H3>
|
||||||
|
<View style={[a.flex_1, t.atoms.border, a.border_t]} />
|
||||||
|
<H3 style={[a.font_bold, t.atoms.text_contrast_500]}>
|
||||||
|
theme.atoms.text_contrast_500
|
||||||
|
</H3>
|
||||||
|
<View style={[a.flex_1, t.atoms.border_contrast_500, a.border_t]} />
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg_contrast_100,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg_contrast_100</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg_contrast_200,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg_contrast_200</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg_contrast_300,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg_contrast_300</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg_positive,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg_positive</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
t.atoms.bg_negative,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{height: 60},
|
||||||
|
]}>
|
||||||
|
<Text>theme.bg_negative</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DebugScreen() {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<CenteredView style={[t.atoms.bg]}>
|
||||||
|
<View style={[a.p_xl, a.gap_xxl, {paddingBottom: 200}]}>
|
||||||
|
<ThemeSelector />
|
||||||
|
|
||||||
|
<Alf theme="light">
|
||||||
|
<ThemedSection />
|
||||||
|
</Alf>
|
||||||
|
<Alf theme="dark">
|
||||||
|
<ThemedSection />
|
||||||
|
</Alf>
|
||||||
|
|
||||||
|
<H1>Heading 1</H1>
|
||||||
|
<H2>Heading 2</H2>
|
||||||
|
<H3>Heading 3</H3>
|
||||||
|
<H4>Heading 4</H4>
|
||||||
|
<H5>Heading 5</H5>
|
||||||
|
<H6>Heading 6</H6>
|
||||||
|
|
||||||
|
<Text style={[a.text_xxl]}>atoms.text_xxl</Text>
|
||||||
|
<Text style={[a.text_xl]}>atoms.text_xl</Text>
|
||||||
|
<Text style={[a.text_lg]}>atoms.text_lg</Text>
|
||||||
|
<Text style={[a.text_md]}>atoms.text_md</Text>
|
||||||
|
<Text style={[a.text_sm]}>atoms.text_sm</Text>
|
||||||
|
<Text style={[a.text_xs]}>atoms.text_xs</Text>
|
||||||
|
<Text style={[a.text_xxs]}>atoms.text_xxs</Text>
|
||||||
|
|
||||||
|
<View style={[a.gap_md, a.align_start]}>
|
||||||
|
<Button>
|
||||||
|
{({state}) => (
|
||||||
|
<View style={[a.p_md, a.rounded_full, t.atoms.bg_contrast_300]}>
|
||||||
|
<Text>Unstyled button, state: {JSON.stringify(state)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button type="primary" size="small">
|
||||||
|
Button
|
||||||
|
</Button>
|
||||||
|
<Button type="secondary" size="small">
|
||||||
|
Button
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button type="primary" size="large">
|
||||||
|
Button
|
||||||
|
</Button>
|
||||||
|
<Button type="secondary" size="large">
|
||||||
|
Button
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button type="secondary" size="small">
|
||||||
|
{({type, size}) => (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={['fas', 'plus']} size={12} />
|
||||||
|
<ButtonText type={type} size={size}>
|
||||||
|
With an icon
|
||||||
|
</ButtonText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" size="large">
|
||||||
|
{({state: _state, ...rest}) => (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={['fas', 'plus']} />
|
||||||
|
<ButtonText {...rest}>With an icon</ButtonText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_0},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_100},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_200},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_300},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_400},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_500},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_600},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_700},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_800},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_900},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.gray_1000},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_0},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_100},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_200},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_300},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_400},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_500},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_600},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_700},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_800},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_900},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.blue_1000},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_0},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_100},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_200},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_300},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_400},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_500},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_600},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_700},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_800},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_900},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.green_1000},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_0},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_100},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_200},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_300},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_400},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_500},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_600},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_700},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_800},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_900},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
{height: 60, backgroundColor: tokens.color.red_1000},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<H3 style={[a.pb_md, a.font_bold]}>Spacing</H3>
|
||||||
|
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>xxs (2px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_xxs, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>xs (4px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_xs, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>sm (8px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_sm, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>md (12px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_md, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>lg (18px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_lg, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>xl (24px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_xl, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
|
<Text style={{width: 80}}>xxl (32px)</Text>
|
||||||
|
<View style={[a.flex_1, a.pt_xxl, t.atoms.bg_contrast_300]} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<BreakpointDebugger />
|
||||||
|
</View>
|
||||||
|
</CenteredView>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue