Skip to content

Commit

Permalink
Add icons docs
Browse files Browse the repository at this point in the history
  • Loading branch information
gabro committed Jun 27, 2023
1 parent d7aa03d commit 7a0f9be
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 16 deletions.
4 changes: 2 additions & 2 deletions packages/bento-design-system/src/Icons/svgIconProps.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SVGProps } from "react";
import { PropsWithoutRef, SVGProps } from "react";
import { iconRecipe } from "./Icon.css";
import { IconProps } from "./IconProps";

export function svgIconProps(
{ size, color = "default" }: IconProps,
sizeAxis: "horizontal" | "vertical" = "horizontal"
): SVGProps<SVGSVGElement> {
): PropsWithoutRef<SVGProps<SVGSVGElement>> {
return {
className:
sizeAxis === "horizontal"
Expand Down
1 change: 1 addition & 0 deletions packages/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"dependencies": {
"@buildo/bento-design-system": "workspace:*",
"@phosphor-icons/react": "^2.0.10",
"@vanilla-extract/sprinkles": "1.6.0",
"date-fns": "2.29.3",
"recharts": "^2.1.16"
Expand Down
23 changes: 22 additions & 1 deletion packages/storybook/stories/Foundations/Icons.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Meta, StoryObj } from "@storybook/react";
import { Box, Inline, Stack, Body, IconProps, icons } from "..";
import { Box, Inline, Stack, Body, IconProps, icons, svgIconProps } from "..";
import { Horse, Icon as PhosphorIcon } from "@phosphor-icons/react";
import { forwardRef } from "react";

const meta = {
title: "Foundations/Icons",
Expand Down Expand Up @@ -58,3 +60,22 @@ export const Icons = {
</Stack>
),
} satisfies Story;

function phosphorToBento(Icon: PhosphorIcon) {
return forwardRef<SVGSVGEelement, IconProps>((props, ref) => {
const { viewBox, ...svgProps } = svgIconProps(props);
return <Icon ref={ref} width={undefined} height={undefined} {...svgProps} />;
});
}

const IconHorse = phosphorWrapper(Horse);

export const PhosphorIcons = {
args: {
color: "brandPrimary",
size: 40,
},
render: (args) => {
return <IconHorse {...args} />;
},
} satisfies Story;
87 changes: 87 additions & 0 deletions packages/website/docs/05-Guides/icons.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Canvas } from "../../src/components/Canvas";

# Icons

Bento comes with a default set of icons, which are React components accepting a set of common props, specifically:

- `size`
- `color`
- `className` (optional)

`size` and `color` are limited to specific semantic values, so to ensure consistency in their usage.

## Changing the icons used in components

You can change any icon used by Bento components using the configuration.

For example here's how you can change the default icon used by the `Chip` component:

<Canvas path="Icons/Configuration" />

For more information on how to configure Bento components, please refer to the [configuration documentation](../Customization/configuration).

## Adding new icons

If you want to add a new icon, you generally proceed as follows:

- create a svg asset with a viewport of `0 0 24 24`
- run it through something like https://react-svgr.com/ to cleanup the markup
- remove all props from the `svg` tag
- remove all `fill` attributes from the svg
- create a component following this template

```tsx
import { IconProps, svgIconProps } from "@buildo/bento-design-system";

export function IconMyCustomName(props: IconProps) {
return <svg {...svgIconProps(props)}>{/* SVG markup gos here*/}</svg>;
}
```

A few things to notice:

- stripping all default props from the `svg` tag is important so that `svgIconProps` can correctly set the icon size and viewbox
- this is true also for `fill` attributes, which are removed so that `svgIconProps` can correctly set the icon color
- it's good practice to name icons with the `Icon` prefix, so that they're easy to lookup in autocompletion
- using `IconProps` ensures that you can directly pass your custom icons to any Bento component that accepts an `icon` prop, for example

```tsx
<Button
kind="solid"
hierarchy="primary"
label="Hello"
onPress={() => window.alert("Hello!")}
// highlight-next-line
icon={IconMyCustomName}
/>
```

## Adding icons from existing icons sets

You can use a similar strategy when adding icons from existing icon sets.
The main thing to look out for is that some icon sets may use different viewbox and set some other props, which may interact badly with `svgIconProps`.
Here's an example of how you can add an icon from the [Phosphor icon set](https://phosphoricons.com/).

First define a utility to convert any icon from the `@phosphor-icons/react` package to a Bento icon:

```tsx
import type { Icon as PhosphorIcon } from "@phosphor-icons/react";

function phosphorToBento(Icon: PhosphorIcon) {
return (props: IconProps) => {
const { viewBox, ...svgProps } = svgIconProps(props);
return <Icon width={undefined} height={undefined} {...svgProps} />;
};
}
```

Then use it to create a Bento icon:

```tsx
import { Horse } from "@phosphor-icons/react";
const IconHorse = phosphorToBento(Horse);
```

Now you can use it as a standalone component or pass it as a prop to a Bento component:

<Canvas path="Icons/Phosphor" showLinkToPlayroom={false} initialShowSource />
1 change: 1 addition & 0 deletions packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@docusaurus/theme-classic": "2.0.1",
"@docusaurus/theme-live-codeblock": "2.0.1",
"@docusaurus/types": "2.0.1",
"@phosphor-icons/react": "^2.0.10",
"@tsconfig/docusaurus": "1.0.6",
"@types/babel__generator": "7.6.4",
"@types/babel__standalone": "7.1.4",
Expand Down
6 changes: 5 additions & 1 deletion packages/website/src/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ const babelPresetTypescript = require("@babel/preset-typescript");
export function Canvas({
path,
initialShowSource = false,
showLinkToPlayroom = true,
}: {
path: string;
initialShowSource: boolean;
showLinkToPlayroom: boolean;
}) {
const [showSource, setShowSource] = useState(initialShowSource);

Expand Down Expand Up @@ -70,7 +72,9 @@ export function Canvas({
onClick={() => setShowSource((s) => !s)}
label={showSource ? "▲ Hide code" : "▼ View code"}
/>
<OpenInPlayroom ast={ast} componentSource={componentSource} />
{showLinkToPlayroom && (
<OpenInPlayroom ast={ast} componentSource={componentSource} />
)}
</div>
</>
)}
Expand Down
19 changes: 19 additions & 0 deletions packages/website/src/snippets/Icons/Configuration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import { BentoConfigProvider, Chip, IconStar, Stack, Title } from "..";

export default function ConfigurationExample() {
return (
<Stack space={32}>
<Stack space={12}>
<Title size="small">Default icon</Title>
<Chip color="indigo" label="Default" onDismiss={() => console.log("dismiss")} />
</Stack>
<Stack space={12}>
<Title size="small">Custom icon icon</Title>
<BentoConfigProvider value={{ chip: { closeIcon: IconStar } }}>
<Chip color="indigo" label="Default" onDismiss={() => console.log("dismiss")} />
</BentoConfigProvider>
</Stack>
</Stack>
);
}
28 changes: 28 additions & 0 deletions packages/website/src/snippets/Icons/Phosphor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import { Button, IconProps, Stack, svgIconProps } from "..";
import { Horse, Icon as PhosphorIcon } from "@phosphor-icons/react";

// NOTE(gabro): if you change this please keep it in sync with the snippet in the documentation markdown.
function phosphorToBento(Icon: PhosphorIcon) {
return (props: IconProps) => {
const { viewBox, ...svgProps } = svgIconProps(props);
return <Icon width={undefined} height={undefined} {...svgProps} />;
};
}

const IconHorse = phosphorToBento(Horse);

export default function PhosphorExample() {
return (
<Stack space={32} align="left">
<IconHorse size={40} color="brandPrimary" />
<Button
kind="solid"
hierarchy="primary"
label="Hello"
onPress={() => window.alert("Hello!")}
icon={IconHorse}
/>
</Stack>
);
}
Loading

0 comments on commit 7a0f9be

Please sign in to comment.