Docs
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
# Application Layout Framework (ALF)
|
||||
|
||||
ALF is a low-level styling system inspired by prior art like
|
||||
[styled-system](https://github.com/styled-system/styled-system) and others.
|
||||
|
||||
It consists of two core parts: one or more **themes** and a single **system**.
|
||||
Although a theme really comes first, you'll most often interact with the system,
|
||||
so we'll start there.
|
||||
|
||||
## System
|
||||
|
||||
The _system_ is just the "layout system", and it's created from a set of themes.
|
||||
|
||||
```typescript
|
||||
import {createSystem} from '#/alf/lib/system'
|
||||
import {light, dark} from '#/alf/themes'
|
||||
|
||||
const {
|
||||
ThemeProvider,
|
||||
styled,
|
||||
useStyle,
|
||||
useStyles,
|
||||
useTheme,
|
||||
useTokens,
|
||||
useBreakpoints,
|
||||
} = createSystem({
|
||||
light,
|
||||
dark,
|
||||
})
|
||||
```
|
||||
|
||||
### ThemeProvider
|
||||
|
||||
The `ThemeProvider` expects a single prop `theme`, which corresponds to the
|
||||
_keys_ of the object passed to `createSystem`, which should correspond to your
|
||||
theme names. Using the above, `theme` should be `light` or `dark`.
|
||||
|
||||
|
||||
```typescript
|
||||
<ThemeProvider theme='light'>...</ThemeProvider>
|
||||
```
|
||||
|
||||
### styled
|
||||
|
||||
Convenience method to create a themeable component from another primitive.
|
||||
Accepts a component and an object of default styles. Typically only be used in a
|
||||
few places, like for creating primitive `Box` and `Text` components.
|
||||
|
||||
**Do not get carried away with this. Multiple levels of nesting is performance
|
||||
intensive, and is hard to debug.**
|
||||
|
||||
```tsx
|
||||
import {styled} from '#/alf/system'
|
||||
|
||||
const Box = styled(View, {})
|
||||
|
||||
<Box c='primary' gtMobile={{ pa: 'm' }} />
|
||||
```
|
||||
|
||||
> An additional feature of `styled` components is a boolean `debug` prop that
|
||||
> will print the processed styles and properties to the console.
|
||||
|
||||
### useStyle
|
||||
|
||||
Mid-level hook to create themed styles with the full theme config at your
|
||||
disposal. Most often helpful when styling 3rd party libraries, such as a
|
||||
dropdown.
|
||||
|
||||
```tsx
|
||||
const styles = useStyle({
|
||||
c: 'primary',
|
||||
gtMobile: {
|
||||
c: 'secondary',
|
||||
},
|
||||
})
|
||||
|
||||
<DropdownItem style={styles} />
|
||||
```
|
||||
|
||||
### useStyles
|
||||
|
||||
Mid-level hook to create _named_ and themed styles with the full theme config at
|
||||
your disposal. Most often helpful when styling 3rd party libraries, such as a
|
||||
dropdown. Think of this as similar to `StyleSheet.create`.
|
||||
|
||||
```typescript
|
||||
const { outer, header } = useStyles({
|
||||
outer: {
|
||||
px: 'm',
|
||||
gtMobile: {
|
||||
px: 'l',
|
||||
},
|
||||
},
|
||||
header: {
|
||||
fontSize: 'xl',
|
||||
}
|
||||
})
|
||||
|
||||
<View style={outer}>
|
||||
<Text style={header}>Hello</Text>
|
||||
</View>
|
||||
```
|
||||
|
||||
### useTheme
|
||||
|
||||
Returns the full currently-active theme object e.g. `light` or `dark`, and all utils attached.
|
||||
Really only used when you need low-level access.
|
||||
|
||||
```typescript
|
||||
const theme = useTheme()
|
||||
const styles = theme.style({
|
||||
c: 'blue',
|
||||
})
|
||||
```
|
||||
|
||||
### useTokens
|
||||
|
||||
Returns just the design tokens of the currently-active theme.
|
||||
|
||||
```tsx
|
||||
const tokens = useTokens()
|
||||
|
||||
<View style={{ color: tokens.color.blue }} />
|
||||
```
|
||||
|
||||
### useBreakpoints
|
||||
|
||||
Returns the current and active breakpoints, which are stored on the theme
|
||||
context.
|
||||
|
||||
```typescript
|
||||
const { current, active } = useBreakpoints()
|
||||
|
||||
// =>
|
||||
{
|
||||
current: 'gtTablet',
|
||||
active: [
|
||||
'gtTablet',
|
||||
'gtMobile',
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Themes
|
||||
|
||||
Themes are made up of a collection of utilities and created from a set of design
|
||||
tokens and other configuration. They are external to React, and can be used
|
||||
directly if low-level access is needed.
|
||||
|
||||
### Creating a theme
|
||||
|
||||
```typescript
|
||||
import {createTheme} from '#/alf/lib/theme'
|
||||
|
||||
const light = createTheme(config)
|
||||
```
|
||||
|
||||
Config consists of:
|
||||
|
||||
#### Tokens
|
||||
|
||||
Tokens a.k.a. "design tokens", are the smallest building block of the design
|
||||
system. The name of each token directly corresponds to the name of the CSS
|
||||
property, with the exception of `space`, which is used as a value source for
|
||||
properties like `width` unless a specific `width` scale is configured.
|
||||
|
||||
```typescript
|
||||
createTheme({
|
||||
tokens: {
|
||||
// special token in ALF
|
||||
space: {
|
||||
s: 8,
|
||||
m: 12,
|
||||
l: 18,
|
||||
},
|
||||
// matches CSS prop name exactly
|
||||
color: {
|
||||
blue: '#0000FF',
|
||||
},
|
||||
// matches CSS prop name exactly
|
||||
fontSize: {
|
||||
s: 14,
|
||||
m: 16,
|
||||
l: 18,
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
Properties are a mapping property names to actual CSS properties. Internally,
|
||||
ALF specifies a mapping of all supported CSS properties. When creating a theme
|
||||
is a time to specify "shorthands" or "aliases" a.k.a. syntax sugar. Docblocks
|
||||
will persist and be available for intellisense.
|
||||
|
||||
```typescript
|
||||
createTheme({
|
||||
tokens: {...},
|
||||
properties: {
|
||||
/** Alias for `width` */
|
||||
w: ['width'],
|
||||
/** Alias for `color` */
|
||||
c: ['color'],
|
||||
/** Alias for all directional margin properties */
|
||||
ma: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Breakpoints
|
||||
|
||||
In ALF, breakpoints are applied as the equivalent of `min-width` CSS media
|
||||
queries, meaning your base styles are mobile, and breakpoints are then applied
|
||||
in order. You can name these anything you want, but we recommend the `gt`
|
||||
prefix; basically "greater than N".
|
||||
|
||||
Given the below, at 1000px wide, both the base and the styles applied in
|
||||
`gtMobile` will be applied.
|
||||
|
||||
|
||||
```typescript
|
||||
createTheme({
|
||||
tokens: {...},
|
||||
properties: {...},
|
||||
breakpoints: {
|
||||
/** Greater than 800 */
|
||||
gtMobile: 800,
|
||||
/** Greater than 1300 */
|
||||
gtTablet: 1300,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Macros
|
||||
|
||||
Macros are further syntax sugar. They can be configured as boolean attributes,
|
||||
as allowing a specific set of values, as simple generic methods, or a
|
||||
combination.
|
||||
|
||||
```typescript
|
||||
createTheme({
|
||||
tokens: {...},
|
||||
properties: {...},
|
||||
breakpoints: {...},
|
||||
macros: {
|
||||
/** Shorthand for `flexDirection: 'row'` */
|
||||
row: (_: boolean) => ({flexDirection: 'row', flex: 1}),
|
||||
/**
|
||||
* Shorthand for `flex: 1`. Optionally pass an integer to specify the
|
||||
* col-span.
|
||||
*
|
||||
* Semantically this is helpful as a direct child of `<Box row>`
|
||||
*
|
||||
* @example
|
||||
* <Box row>
|
||||
* <Box column>
|
||||
* <Text>Hello</Text>
|
||||
* </Box>
|
||||
* <Box column={2}>
|
||||
* <Text>Hello</Text>
|
||||
* </Box>
|
||||
* </Box>
|
||||
*/
|
||||
column: (span: boolean | number) => ({
|
||||
flex: typeof span === 'number' ? span : 1,
|
||||
}),
|
||||
/** Shorthand for `alignItems: 'center'` */
|
||||
aic: (_: boolean) => ({alignItems: 'center'}),
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Given the above config, creating a grid with `Box` (see next section) is as
|
||||
simple as:
|
||||
|
||||
```tsx
|
||||
<Box row aic>
|
||||
{/* 1/4 width */}
|
||||
<Box column></Box>
|
||||
|
||||
{/* 1/2 width */}
|
||||
<Box column={2}></Box>
|
||||
|
||||
{/* 1/4 width */}
|
||||
<Box column></Box>
|
||||
</Box>
|
||||
```
|
||||
|
||||
## Performance considerations
|
||||
|
||||
### Memoize style objects
|
||||
|
||||
For primitive components or those that render often, it's probably a good idea
|
||||
to memoize your style objects prior to passing them to one of the hooks or
|
||||
`Box`.
|
||||
|
||||
```typescript
|
||||
function Component(props) {
|
||||
const styles = useStyle(React.useMemo(() => ({
|
||||
color: props.prop ? 'blue' : 'red',
|
||||
}), [props.prop]))
|
||||
|
||||
return <View style={styles} />
|
||||
}
|
||||
```
|
||||
|
||||
### Pre-define styles for list views
|
||||
|
||||
For some simple `FlatList`s, you may be able to pre-define styles for list
|
||||
components, and pass them in as properties, instead of computing new styles for
|
||||
each component.
|
||||
Reference in New Issue
Block a user