mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
The loader and the Next plugin only existed to drive webpack; Next runs on Turbopack and Vite has its own plugin. kitchen-sink and the integration app serve through vite, the optimizer suite drops the webpack name, and its fixture packages join the workspace so a fresh checkout links @my/ui.
225 lines
5.5 KiB
Plaintext
225 lines
5.5 KiB
Plaintext
---
|
|
title: Design Systems
|
|
description: Put together your own design system.
|
|
---
|
|
|
|
Gui allows you to build out your own set of components that are optimized
|
|
with the compiler whenever they are used in your app.
|
|
|
|
By default, if you just use `styled()` in your app, Gui **won't** be able to
|
|
optimize those components. This is because the compiler needs to know about
|
|
those components at build-time.
|
|
|
|
Let's break down how to set this up in more detail.
|
|
|
|
## Step 1: Create a package
|
|
|
|
Your design system needs to live in its own npm module, which can be private to
|
|
just your app. That way you can later direct the compiler to look for that
|
|
package.
|
|
|
|
Design systems can extend from each other. In fact, `hanzogui` extends
|
|
`@hanzogui/core`, which contains simple base-level components.
|
|
|
|
So, for example, if you'd like to use the Gui `XStack`, `YStack`, `Button`
|
|
and `Paragraph` in your design system, you would add `hanzogui` to your design
|
|
system's package.json.
|
|
|
|
If you want to build more from scratch, then use `@hanzogui/core` and only import
|
|
either the `View` or `Text` components. For the purpose of this guide, we'll use
|
|
`@hanzogui/core`.
|
|
|
|
Add package.json:
|
|
|
|
```json
|
|
{
|
|
"name": "@ourapp/components",
|
|
"types": "./types/index.d.ts",
|
|
"main": "dist/cjs",
|
|
"module": "dist/esm",
|
|
"module:jsx": "dist/jsx",
|
|
"files": ["types", "src", "dist"],
|
|
"sideEffects": ["*.css"],
|
|
"dependencies": {
|
|
"@hanzogui/core": "*"
|
|
},
|
|
"scripts": {
|
|
"build": "hanzogui-build",
|
|
"watch": "hanzogui-build --watch"
|
|
},
|
|
"devDependencies": {
|
|
"@hanzogui/build": "*"
|
|
}
|
|
}
|
|
```
|
|
|
|
If using Typescript, a `tsconfig.json` of your choosing:
|
|
|
|
```json fileName="tsconfig.json"
|
|
{
|
|
"compilerOptions": {
|
|
"types": ["node", "react"],
|
|
"lib": ["dom", "esnext"],
|
|
"baseUrl": "./",
|
|
"outDir": "dist"
|
|
},
|
|
"include": ["./**/*.ts"],
|
|
"exclude": ["node_modules/**/*"]
|
|
}
|
|
```
|
|
|
|
There are a few things to note here:
|
|
|
|
- We're using `@hanzogui/build` to build this package, which is a small script
|
|
built around `esbuild` and `typescript` that makes sure you output your
|
|
components _with JSX preserved_.
|
|
- `module:jsx` points the compiler at that JSX-preserving build; it resolves the
|
|
field itself, so your bundler needs no configuration for it.
|
|
- `sideEffects` matters: a bundler that tree-shakes would otherwise drop the
|
|
generated CSS in production.
|
|
|
|
## Step 2: Create your design system
|
|
|
|
Check the [configuration](/docs/core/configuration) for more detail on this
|
|
step.
|
|
|
|
You'll be creating a `hanzogui.config.ts` at the root of your app. It will
|
|
contain a full suite of tokens, themes, and fonts exported onto a single named
|
|
`config` export.
|
|
|
|
## Step 3: Define and export components
|
|
|
|
Now, create and export your components. You can re-export components from
|
|
`hanzogui` or `@hanzogui/core` as well. Let's create a Circle component:
|
|
|
|
`Circle.tsx`:
|
|
|
|
```tsx
|
|
import { GetProps, YStack, styled } from '@hanzo/gui' // or '@hanzogui/core' if extending just that
|
|
|
|
export const Circle = styled(YStack, {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderRadius: 100_000_000,
|
|
overflow: 'hidden',
|
|
|
|
variants: {
|
|
size: {
|
|
'...size': (size, { tokens }) => {
|
|
return {
|
|
width: tokens.size[size] ?? size,
|
|
height: tokens.size[size] ?? size,
|
|
}
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
export type CircleProps = GetProps<typeof Circle>
|
|
```
|
|
|
|
And then export from `index.tsx`:
|
|
|
|
```tsx
|
|
export * from './Circle'
|
|
```
|
|
|
|
## Step 4: Set up your build
|
|
|
|
Now in your app, add `@ourapp/components` and `hanzogui` (since we are extending
|
|
it) to your package.json, and update your hanzogui build configuration.
|
|
|
|
### hanzogui.build.ts
|
|
|
|
Every plugin and the CLI read this file:
|
|
|
|
```ts fileName="hanzogui.build.ts"
|
|
import type { GuiBuildOptions } from '@hanzo/gui'
|
|
|
|
export default {
|
|
config: './hanzogui.config.ts',
|
|
components: ['@ourapp/components', '@hanzo/gui'],
|
|
} satisfies GuiBuildOptions
|
|
```
|
|
|
|
### Vite
|
|
|
|
```ts fileName="vite.config.ts"
|
|
import { hanzoguiPlugin } from '@hanzogui/vite-plugin'
|
|
|
|
export default defineConfig({
|
|
plugins: [hanzoguiPlugin()],
|
|
})
|
|
```
|
|
|
|
### Next.js
|
|
|
|
Turbopack needs no plugin; the CLI optimizes ahead of the build:
|
|
|
|
```json fileName="package.json"
|
|
{
|
|
"scripts": {
|
|
"build": "hanzogui build --target web ./src -- next build"
|
|
}
|
|
}
|
|
```
|
|
|
|
### React Native
|
|
|
|
In your `babel.config.js`:
|
|
|
|
```js
|
|
export default {
|
|
plugins: [
|
|
[
|
|
'@hanzogui/babel-plugin',
|
|
{
|
|
exclude: /node_modules/,
|
|
config: './hanzogui.config.ts',
|
|
components: ['@ourapp/components', '@hanzo/gui'],
|
|
},
|
|
],
|
|
],
|
|
}
|
|
```
|
|
|
|
### Configuration notes
|
|
|
|
You only need `hanzogui` in your `components` array if you extend it, otherwise
|
|
no need. You can also extend your own base level module so long as they export
|
|
Gui `styled` components.
|
|
|
|
## Step 5: Test it out
|
|
|
|
In your app, you should now be able to import and use your Circle component.
|
|
Using the debug pragma, you can also verify the extraction is working. Make sure
|
|
the build settings `logTimings: true` and `disableExtraction: false` are set so
|
|
you can see the compiler at work:
|
|
|
|
Anywhere in your app:
|
|
|
|
```tsx
|
|
import { Circle } from '@ourapp/components'
|
|
|
|
export default () => <Circle size="$large" />
|
|
```
|
|
|
|
When it compiles you should see something like:
|
|
|
|
```bash
|
|
hanzogui built app.tsx in 16ms (1 optimized, 1 flattened)
|
|
```
|
|
|
|
To get more information on any extraction, use the `// debug` pragma:
|
|
|
|
```tsx
|
|
// debug
|
|
// ^ the above pragma will direct Gui to output a lot of information on the extraction
|
|
import { Circle } from '@ourapp/components'
|
|
|
|
export default () => <Circle size="$large" />
|
|
```
|
|
|
|
You should see much more log output with details on how it extracted, including
|
|
the final CSS and JS.
|