0.2.0 • Published 7 months ago
@lmnl/core v0.2.0
lmnl
Getting Started
Create theme(s):
import { createTheme } from "@lmnl/core";
export const light = createTheme({
tokens: {
space: {
s: 8,
m: 16,
l: 24,
},
color: {
theme: "#ff4567",
text: "#333",
},
fontSize: {
s: 18,
m: 22,
l: 26,
},
lineHeight: {
s: 18,
m: 22,
l: 26,
},
fontFamily: {
sans: "sans-serif",
mono: "monospace",
},
borderRadius: {
s: 4,
m: 8,
l: 16,
},
},
properties: {
d: ["display"],
w: ["width"],
h: ["height"],
c: ["color"],
bg: ["backgroundColor"],
ma: ["marginTop", "marginBottom", "marginLeft", "marginRight"],
mt: ["marginTop"],
mb: ["marginBottom"],
ml: ["marginLeft"],
mr: ["marginRight"],
my: ["marginTop", "marginBottom"],
mx: ["marginLeft", "marginRight"],
pa: ["paddingTop", "paddingBottom", "paddingLeft", "paddingRight"],
pt: ["paddingTop"],
pb: ["paddingBottom"],
pl: ["paddingLeft"],
pr: ["paddingRight"],
py: ["paddingTop", "paddingBottom"],
px: ["paddingLeft", "paddingRight"],
z: ["zIndex"],
fs: ["fontSize"],
ff: ["fontFamily"],
fw: ["fontWeight"],
lh: ["lineHeight"],
ta: ["textAlign"],
radius: ["borderRadius"],
},
macros: {
row: (_: boolean) => ({ flexDirection: "row" }),
column: (_: boolean) => ({ flex: 1 }),
aic: (_: boolean) => ({ alignItems: "center" }),
aie: (_: boolean) => ({ alignItems: "flex-end" }),
jcs: (_: boolean) => ({ justifyContent: "flex-start" }),
jcc: (_: boolean) => ({ justifyContent: "center" }),
jce: (_: boolean) => ({ justifyContent: "flex-end" }),
jcb: (_: boolean) => ({ justifyContent: "space-between" }),
rel: (_: boolean) => ({ position: "relative" }),
abs: (_: boolean) => ({ position: "absolute" }),
cover: (_: boolean) => ({
top: 0,
bottom: 0,
left: 0,
right: 0,
}),
tac: (_: boolean) => ({ textAlign: "center" }),
tar: (_: boolean) => ({ textAlign: "right" }),
mxa: (_: boolean) => ({ marginLeft: "auto", marginRight: "auto" }),
mya: (_: boolean) => ({ marginTop: "auto", marginBottom: "auto" }),
caps: (_: boolean) => ({ textTransform: "uppercase" }),
font(value: "black" | "bold" | "regular", tokens) {
const variant = {
black: {
fontFamily: "inter_black",
},
bold: {
fontFamily: "inter_bold",
},
regular: {
fontFamily: "inter_regular",
},
}[value];
return {
...variant,
};
},
fontSize(value: "s" | "m" | "l", tokens) {
return {
fontSize: tokens.fontSize[value],
lineHeight: tokens.lineHeight[value],
};
},
},
breakpoints: {
gtPhone: 640,
},
});
export const dark = createTheme({
...light.config,
tokens: {
...light.config.tokens,
color: {
...light.config.tokens.color,
text: "#fff",
},
},
});
And then create a system. The boilerplate is intentional, it allows for greater flexibility.
// system.ts
import { createSystem } from "@lmnl/core";
import { light, dark } from "./themes";
const {
ThemeProvider,
useTheme,
useBreakpoints,
usePick,
useStyle,
useStyles,
styled,
} = createSystem({
light,
dark,
});
// export for use in your app
export {
ThemeProvider,
useTheme,
useBreakpoints,
usePick,
useStyle,
useStyles,
styled,
};
// export some base components
export const Box = styled(View, {});
export type BoxProps = React.ComponentProps<typeof Box>;
export const Text = styled(RNText, {
color: "text",
});
export type TextProps = React.ComponentProps<typeof Text>;
Creating more complex components is pretty easy too. Here's an example of a
semantic HTML component (requires react-native-web
):
import { Text as RNText, Platform } from "react-native";
/**
* @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, {
role: web("heading"),
color: "text",
font: "black",
fontSize: "m",
gtPhone: {
fontSize: "l",
},
...Platform.select({
web: {
"aria-level": 1,
},
}),
});
Usage
import { Image, View } from "react-native";
import { web } from "@lmnl/core";
import {
ThemeProvider,
Box,
Text,
styled,
useStyle,
useStyles,
} from "./system";
const Img = styled(Image, {});
export function App() {
const styles = useStyle({
c: "theme",
fs: "m",
gtPhone: {
fs: "l",
},
});
const { outerStyles, innerStyles } = useStyles({
outerStyles: {
px: "m",
h: web("100vh"),
gtPhone: {
px: "l",
},
},
innerStyles: {
pt: "m",
},
});
return (
<ThemeProvider theme="light">
<View style={outerStyles}>
<View style={innerStyles}>
<Box pb="m" gtPhone={{ pb: "l" }}>
<Text c="text" fs="l">
Hello world
</Text>
<Img mt="s" source={{ uri: "https://picsum.photos/200/300" }} />
</Box>
</View>
</View>
</ThemeProvider>
);
}