Add a readme

This commit is contained in:
Zoe Roux
2022-11-13 00:15:12 +09:00
parent 3c6e58b6b7
commit 77aa4606d8
13 changed files with 308 additions and 33 deletions

2
.gitignore vendored
View File

@@ -42,4 +42,4 @@ yarn-error.log*
!.yarn/sdks
.expo
dist/

213
README.md Normal file
View File

@@ -0,0 +1,213 @@
# yoshiki
## Features
- Same interface for React and React Native
- Universal API working for builtins, any component library or your own
- Breakpoints as objects
- User defined theme support
- Shorthands (`m` for `margin`, `paddingX` for `paddingLeft` and `paddingRight`...)
- Atomic CSS generation
- SSR support
## Usage
```tsx
import { Stylable, useYoshiki } from "yoshiki";
const ColoredDiv = ({ color }: { color: string }) => {
const { css } = useYoshiki();
return (
<div
{...css({
backgroundColor: color,
height: { xs: "13%", lg: "25%" },
paddingX: (theme) => theme.spaccing,
m: 1,
})}
>
<p {...css({ color: "white" })}>Text inside the colored div.</p>
</div>
);
};
```
Or for React Native components, simply use the `yoshiki/native` import.
Notice that the only difference between the two are the components and the import.
```tsx
import { Text, View } from "react-native";
import { Stylable, useYoshiki } from "yoshiki/native";
const ColoredBox = ({ color }: { color: string }) => {
const { css } = useYoshiki();
return (
<View
{...css({
backgroundColor: color,
height: { xs: "13%", lg: "25%" },
paddingX: (theme) => theme.spaccing,
m: 1,
})}
>
<Text {...css({ color: "white" })}>Text inside the colored box.</Text>
</View>
);
};
```
## Recipes
### Customize your own components
In order to theme your own components, you need to forward some props to the root element like the following example:
```tsx
const Example = (props) => {
return (
<div {...props}>
<p>Example component</p>
</div>
);
};
```
If you want to use yoshiki to theme your component and allow others components to override styles, pass
the props to the `css` function.
```tsx
import { useYoshiki } from "yoshiki";
const Example = (props) => {
const { css } = useYoshiki();
return (
<div {...css({ background: "black" }, props)}>
<p>Example component</p>
</div>
);
};
```
To stay type-safe and ensure you don't forget to pass down the props, yoshiki exposes the `Stylable` type, so you can do:
```tsx
import { ReactNode } from "react";
import { useYoshiki, Stylable } from "yoshiki";
// or
// import { useYoshiki, Stylable } from "yoshiki/native";
const Example = (props: Stylable) => {
const { css } = useYoshiki();
return (
<div {...css({ background: "black" }, props)}>
<ExampleText {...css({ padding: "15px" })}>Example component</ExampleText>
</div>
);
};
const ExampleText = ({ children, ...props }: { children: ReactNode } & Stylable) => {
const { css } = useYoshiki();
return <p {...css({ color: "white", padding: "10px" }, props)}>{children}</p>;
};
```
Yoshiki will handle overrides so the `ExampleText`'s p element will have a padding of `15px`.
### Server Side Rendering (SSR)
#### Generic
To support server side rendering, you need to create a style registry and wrap your app with a `StyleRegistryProvider`.
```tsx
import { StyleRegistryProvider, createStyleRegistry } from "yoshiki";
const registry = createStyleRegistry();
const html = renderToString(
<StyleRegistryProvider registry={registry}>
<App />
</StyleRegistryProvider>,
);
// A list of classes to append to your html head.
const style = registry.flush();
```
#### Next
Simply use the following `getInitialProps` inside the `pages/_document.tsx` file.
```tsx
import { StyleRegistryProvider, createStyleRegistry } from "yoshiki";
import Document, { DocumentContext } from "next/document";
Document.getInitialProps = async (ctx: DocumentContext) => {
const renderPage = ctx.renderPage;
const registry = createStyleRegistry();
ctx.renderPage = () =>
renderPage({
enhanceApp: (App) => (props) => {
return (
<StyleRegistryProvider registry={registry}>
<App {...props} />
</StyleRegistryProvider>
);
},
});
const props = await ctx.defaultGetInitialProps(ctx);
return {
...props,
styles: (
<>
{props.styles}
{registry.flushToComponent()}
</>
),
};
};
export default Document;
```
### Theme
To use the theme, you need to wrap your app with a `ThemeProvider`. If you are using typescript, you will also
need to use module augmentation to add your properties to the theme object.
```tsx
import { Theme, ThemeProvider, useYoshiki } from "yoshiki";
declare module "yoshiki" {
export interface Theme {
spacing: string;
name: string;
}
}
const defaultTheme: Theme = {
spacing: "24px",
name: "yoshiki",
};
const App = () => {
return (
<ThemeProvider theme={defaultTheme}>
<AppName />
</ThemeProvider>
);
};
const AppName = () => {
const { css, theme } = useYoshiki();
return <p {...css({ padding: (theme) => theme.spacing })}>{theme.name}</p>;
};
```

View File

@@ -0,0 +1,36 @@
//
// Copyright (c) Zoe Roux and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
import { Theme, ThemeProvider, useYoshiki } from "yoshiki";
import { AppProps } from "next/app";
declare module "yoshiki" {
export interface Theme {
spacing: string;
name: string;
}
}
export const theme: Theme = {
spacing: "24px",
name: "yoshiki",
};
const AppName = () => {
const { css, theme } = useYoshiki();
return <p {...css({ padding: (theme) => theme.spacing })}>{theme.name}</p>;
};
const App = ({ Component, pageProps }: AppProps) => {
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
<AppName />
</ThemeProvider>
);
};
export default App;

View File

@@ -6,6 +6,7 @@
"license": "MIT",
"private": true,
"scripts": {
"build": "yarn workspace yoshiki build",
"next": "yarn workspace next-example dev",
"expo": "yarn workspace expo-example dev",
"lint": "eslint . --ext .ts,.tsx "

View File

@@ -1,11 +1,28 @@
{
"name": "yoshiki",
"version": "1.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"version": "0.1.0",
"author": "Zoe Roux <zoe.roux@sdg.moe> (https://github.com/AnonymusRaccoon)",
"license": "MIT",
"keywords": [
"react",
"react-native",
"css-in-js"
],
"repository": "https://github.com/AnonymusRaccoon/yoshiki",
"homepage": "https://github.com/AnonymusRaccoon/yoshiki",
"bugs": {
"url": "https://github.com/AnonymusRaccoon/yoshiki/issues"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"source": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./native": "./src/native/index.ts"
".": "./dist/index.ts",
"./native": "./dist/native/index.ts",
"./web": "./dist/index.ts"
},
"scripts": {
"build": "tsc --build ."
},
"packageManager": "yarn@3.2.4",
"peerDependencies": {

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
export { type Theme, breakpoints, useTheme } from "./theme";
export { type Theme, breakpoints, useTheme, ThemeProvider, defaultTheme } from "./theme";
export {
useYoshiki,
@@ -11,5 +11,4 @@ export {
StyleRegistryProvider,
createStyleRegistry,
type Stylable,
type CssObject,
} from "./react";

View File

@@ -12,10 +12,6 @@ import { isBreakpoints } from "../utils";
type EnhancedStyle<Properties> = {
[key in keyof Properties]: YoshikiStyle<Properties[key]>;
};
export type CssObject =
| EnhancedStyle<ViewStyle>
| EnhancedStyle<TextStyle>
| EnhancedStyle<ImageStyle>;
type Properties = ViewStyle | TextStyle | ImageStyle;
const useBreakpoint = (): number => {
@@ -51,10 +47,13 @@ export const useYoshiki = () => {
const theme = useTheme();
return {
css: (css: CssObject, leftOvers?: { style?: Properties }) => {
css: <Style extends ViewStyle | TextStyle | ImageStyle>(
css: EnhancedStyle<Style>,
leftOvers?: { style?: Style },
): { style: Style } => {
const { style, ...leftOverProps } = leftOvers ?? {};
const inline: Properties = Object.fromEntries(
const inline: Style = Object.fromEntries(
Object.entries(css)
.map(([key, value]) => [key, propertyMapper(value, { breakpoint, theme })])
.filter(([, value]) => value !== undefined),
@@ -70,5 +69,5 @@ export const useYoshiki = () => {
};
export type Stylable = {
style: Properties
}
style: Properties;
};

View File

@@ -3,6 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
export { type Theme, breakpoints, useTheme } from "../theme";
export { type Theme, breakpoints, useTheme, ThemeProvider, defaultTheme } from "../theme";
export { useYoshiki, type Stylable, type CssObject } from "./generator";
export { useYoshiki, type Stylable } from "./generator";

View File

@@ -8,7 +8,11 @@ import { YoshikiStyle } from "../type";
import { isBreakpoints } from "../utils";
import { CSSProperties, useInsertionEffect } from "react";
import { useStyleRegistry } from "./registry";
import type { CssObject } from ".";
import { Properties } from "csstype";
export type CssObject = {
[key in keyof Properties]: YoshikiStyle<Properties[key]>;
};
const generateAtomicCss = <Property extends number | boolean | string | undefined>(
key: string,

View File

@@ -3,13 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
import { Properties } from "csstype";
import { CSSProperties } from "react";
import { YoshikiStyle } from "../type";
export type CssObject = {
[key in keyof Properties]: YoshikiStyle<Properties[key]>;
};
export type Stylable = {
className?: string;

View File

@@ -3,8 +3,10 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
import { createContext, ReactNode, useContext } from "react";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Theme {};
export interface Theme {}
export const breakpoints = {
xs: 0,
@@ -12,8 +14,12 @@ export const breakpoints = {
md: 900,
lg: 1200,
xl: 1600,
}
export const useTheme = () => {
return {} as Theme;
};
const ThemeContext = createContext({});
export const useTheme = () => useContext(ThemeContext);
export const ThemeProvider = ({ theme, children }: { theme: Theme; children?: ReactNode }) => {
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
};

View File

@@ -0,0 +1,6 @@
//
// Copyright (c) Zoe Roux and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
export * from "./index";

View File

@@ -4,7 +4,6 @@
"lib": ["dom", "dom.iterable", "esnext"],
"declaration": true,
"sourceMap": true,
"noEmit": true,
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -16,8 +15,9 @@
"isolatedModules": true,
"jsx": "react-native",
"incremental": true,
"rootDir": "src/"
"rootDir": "src/",
"outDir": "dist/"
},
"include": ["**/*.ts", "**/*.tsx"],
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}