Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Styleguide #2

Merged
merged 4 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 123 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ yarn add @networkteam/zebra-utils
// npm install @networkteam/zebra-utils
```

## Table of contents

- [Zebra](#zebra)
- [ImgProxy](#imgproxy)
- [Styleguide](#styleguide)
- [Utils](#utils)

## Zebra

### Revalidation

The `revalidate` function is used inside an API route to revalidate the document cache of Next.js. It compares the bearer token with a `REVALIDATE_TOKEN` environment variable.

#### Environment variable
#### Environment variables

```
REVALIDATE_TOKEN=
Expand Down Expand Up @@ -73,7 +80,7 @@ import NotFound from 'app/[[...slug]]/not-found';
export default localizedPage(NotFound, 'fr');
```

### Folder Structure
#### Folder Structure

For the above approach to work, the folder structure needs to look like this. Note: there must be **no** other layout or not-found file in the root of the app directory:

Expand All @@ -100,19 +107,11 @@ app

## ImgProxy

The next config provide a way to use a custom image loader for all (next)-images. It's possible to define custom imageSizes and deviceSizes as well, which are used to create the srcset of the responsive image. We restrict images with dimensions outside of these widths. By default the image loader middleware in this package uses the default image sizes concated with the default devices sizes.

### Environment variables

```
IMGPROXY_URL=
IMGPROXY_KEY=
IMGPROXY_SALT=
```
The next config provides a way to use a custom image loader for all (next)-images. It's possible to define custom imageSizes and deviceSizes as well, which are used to create the srcset of the responsive image. The image loader middleware restricts images with dimensions outside of these widths. It uses the default image sizes concatenated with the default device sizes used by Next.js.

### Loader

Unfortunately its not possible to pass a loader function directly to the next config, it has to be a file path. So create a `imageLoader.ts` inside of the Next.js root directory (or somewhere in the project), with the following content:
Unfortunately, it's not possible to pass a loader function directly to the next config; it has to be a file path. So, create an `imageLoade.ts` inside of the Next.js root directory (or somewhere else in the project), with the following content:

```ts
// imageLoader.ts
Expand All @@ -121,7 +120,7 @@ import { imgProxyLoader } from '@networkteam/zebra-utils';
export default imgProxyLoader('_img');
```

The imgProxyLoader creates a path with the provided width and quality (through the next image), as well as the base64 encoded src. The function takes a path segment (`_img` by default) which has to match the path segment in the following middleware.
The `imgProxyLoader` creates a path with the provided width and quality (through the next image), as well as the base64 encoded src. The function takes a path segment (`_img` by default) which has to match the path segment in the following middleware.

### Middleware

Expand All @@ -146,11 +145,11 @@ export const config = {
};
```

The middleware catches all paths starting with `/_img`, so all paths created by the imgproxy loader. If th Make sure the path segment matches the provided path segment of the imgproxy loader. Beside the request, imgProxyMiddleware takes `ImgProxyMiddlewareOptions`, where the allowedWiths could be overwritten.
The middleware catches all paths starting with `/_img`, so all paths created by the imgproxy loader. Make sure the path segment matches the provided path segment of the imgproxy loader. Besides the request, `imgProxyMiddleware` takes `ImgProxyMiddlewareOptions`, where the allowedWidths could be overwritten.

### Next config

The last part is to add the image loader to the next.config.js:
The last part is to add the image loader to the `next.config.js`:

```ts
// next.config.js
Expand All @@ -165,79 +164,145 @@ const config = {
};
```

Its possible to define custom imageSizes/devicesSizes. Make sure to pass all sizes to the imgProxyMiddleware as well.
It's possible to define custom imageSizes/deviceSizes. Make sure to pass all sizes to the imgProxyMiddleware as well.

## ImgProxy
## Styleguide

The next config provides a way to use a custom image loader for all (next)-images. It's possible to define custom imageSizes and deviceSizes as well, which are used to create the srcset of the responsive image. The image loader middleware restricts images with dimensions outside of these widths. It uses the default image sizes concatenated with the default device sizes used by Next.js.
This package contains a styleguide which is easy to set up for all components of the project. To enable the styleguide, set the following environment variable to any truthy value:

### Loader
### Environment variables

Unfortunately, it's not possible to pass a loader function directly to the next config; it has to be a file path. So, create an `imageLoade.ts` inside of the Next.js root directory (or somewhere else in the project), with the following content:
```
ENABLE_STYLEGUIDE="true"
```

```ts
// imageLoader.ts
import { imgProxyLoader } from '@networkteam/zebra-utils';
### Route

export default imgProxyLoader('_img');
```
First, create a folder structure for the styleguide in the app directory:

The `imgProxyLoader` creates a path with the provided width and quality (through the next image), as well as the base64 encoded src. The function takes a path segment (`_img` by default) which has to match the path segment in the following middleware.
```
app
└── styleguide
└── [[...slug]]
├── content.tsx
├── layout.tsx
└── page.tsx

### Middleware
```

Create a `middleware.ts` inside of the Next.js root directory with the following content:
Assuming all styles and fonts are imported in the project's RootLayout, we can simply import the RootLayout into the styleguide's layout:

```ts
// middleware.ts
import { imgProxyMiddleware } from '@networkteam/zebra-utils';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import RootLayout from 'app/[[...slug]]/layout';
import { localizedPage } from '@networkteam/zebra-utils';

export const middleware = async (request: NextRequest) => {
if (request.nextUrl.pathname.startsWith('/_img/')) {
return imgProxyMiddleware(request);
}
export default RootLayout;

return NextResponse.next();
};
// With multiple languages use the localizedPage helper:
// export default localizedPage(RootLayout);
```

export const config = {
matcher: '/_img/:path*',
};
In the page.tsx export the Styleguide route provided by this package:

```ts
import { Styleguide } from '@networkteam/zebra-utils';
import { content } from './content';

export default Styleguide(content);
```

The middleware catches all paths starting with `/_img`, so all paths created by the imgproxy loader. Make sure the path segment matches the provided path segment of the imgproxy loader. Besides the request, `imgProxyMiddleware` takes `ImgProxyMiddlewareOptions`, where the allowedWidths could be overwritten.
It's possible to use a different subpath for the styleguide, but make sure to pass the path to the `Styleguide` function as the second parameter, like `Styleguide(content, '/custom-path');`. It has to match the root folder name of the styleguide.

### Next config
### Content

The last part is to add the image loader to the `next.config.js`:
The content.tsx contains the structure and all components for the styleguide:

```ts
// next.config.js
const config = {
images: {
formats: ['image/avif', 'image/webp'],
loader: 'custom',
loaderFile: './imageLoader.ts',
// deviceSizes,
// imageSizes,
},
};
import { StyleguideColors, StyleguideContent } from '@networkteam/zebra-utils';

import Logo from 'lib/components/Logo';
import Signet from 'lib/components/Signet';
import TeaserCard from 'lib/components/TeaserCard';

import tailwindConfig from 'tailwind.config';

export const content: StyleguideContent = {
title: 'Styleguide',
description: 'My awesome styleguide',
pages: [
{
path: 'atoms',
title: 'Atoms',
description: 'Atoms are the smallest building blocks of a design system.',
pages: [
{
title: 'Logo',
variants: [
{
title: 'Default',
component: <Logo />,
},
{
title: 'Signet',
component: <Signet />,
},
],
},
{
title: 'Colors',
variants: [
{
component: <StyleguideColors tailwindConfig={tailwindConfig} groupShades />,
},
],
},
]
},
{
path: 'molecules',
title: 'Molecules',
description:
'Molecules are groups of atoms bonded together and are the smallest fundamental units of a compound.',
pages: [
{
title: 'Teaser Card',
variants: [
{
component: (
<TeaserCard
image="https://placebear.com/600/600"
headline="This is a Teaser"
text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr..."
buttonLabel="read more"
href="#"
/>
),
},
],
},
]
},
{
title: 'Organisms',
description:
'Organisms are relatively complex components composed of groups of molecules and/or atoms and/or other organisms.',
pages: []
}
]
```

It's possible to define custom imageSizes/deviceSizes. Make sure to pass all sizes to the imgProxyMiddleware as well.
This example follows the atomic design approach, but subpages could be named anything. The pages path is by default the page title (slugified), but could be explicitly set. Note that the Colors atom uses a custom function `StyleguideColors`, which extracts all colors of a passed `tailwindConfig` and displays them in a grid.

## Utils

These are some helper functions which are often used in Zebra projects.

### cn

This package provides a function named _cn_, based on the same-named function provided by _shadcn/ui_. It uses [clsx](https://github.com/lukeed/clsx) in combination with [tailwind-merge](https://github.com/gjtorikian/tailwind_merge). _tailwind-merge_ merges Tailwind CSS classes to prevent style conflicts. Use it the same way as _classNames_ or _clsx_:
This package provides a function named `cn`, based on the same-named function provided by `shadcn/ui`. It uses [clsx](https://github.com/lukeed/clsx) in combination with [tailwind-merge](https://github.com/gjtorikian/tailwind_merge). `tailwind-merge` merges Tailwind CSS classes to prevent style conflicts. Use it the same way as `classNames` or `clsx`:

```ts

<div
className={cn('p-4 rounded-lg',
{
Expand Down Expand Up @@ -287,7 +352,7 @@ const defaultSizes = new Map([
]);
```

For example, with the default sizes, when setting `marginMd` to `topLarge`, the resulting class would be `md:mt-28`. The sizes can be overwritten with the second argument of the `baseClasses` helper.
With the default sizes, when setting `marginMd` to `topLarge`, the resulting class would be `md:mt-28`. The sizes can be overwritten with the second argument of the `baseClasses` helper.

### slugify

Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
".": "./dist/index.js"
},
"scripts": {
"build": "yarn clean && yarn tsc --project tsconfig.json && yarn babel --config-file ./config/babel.config.js src --out-dir dist --extensions \".tsx,.ts,.js,.jsx\"",
"build": "yarn clean && yarn ts:compile && yarn babel:compile && yarn styleguide:build",
"ts:compile": "yarn tsc --project tsconfig.json",
"babel:compile": "yarn babel --config-file ./config/babel.config.js src --out-dir dist --extensions \".tsx,.ts,.js,.jsx\"",
"clean": "rm -rf dist",
"dev": "yarn clean && yarn watch",
"watch": "yarn tsc --project tsconfig.dev.json --watch",
"styleguide:watch": "npx tailwindcss -c tailwind.config.styleguide.js -o dist/styleguide/styles.css --watch",
"styleguide:build": "npx tailwindcss -c tailwind.config.styleguide.js -o dist/styleguide/styles.css",
"lint": "eslint src config"
},
"dependencies": {
"@babel/runtime": "^7.24.7"
"clsx": "^2.1.1",
"tailwind-merge": "^2.3.0"
},
"devDependencies": {
"@babel/cli": "^7.24.7",
Expand All @@ -34,13 +39,13 @@
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@babel/runtime": "^7.24.7",
"@networkteam/zebra": "^1.0.0",
"@types/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"clsx": "^2.1.1",
"eslint": "^9.5.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
Expand All @@ -49,7 +54,7 @@
"prettier": "^3.3.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.2"
},
"peerDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export { default as imgProxyMiddleware } from './imgproxy/middleware';
export { urlSafeBase64, hexDecode, sign } from './imgproxy/utils';
export { allowedWidths, defaultOptions } from './imgproxy/config';

// Styleguide
export { default as Styleguide } from './styleguide/Route';
export { default as StyleguideColors } from './styleguide/Colors';

// Utils
export { baseClasses, marginClasses, paddingClasses } from './utils/baseClasses';
export { cn } from './utils/classnames';
Expand Down
Loading
Loading