diff --git a/packages/paste-core/components/button/__tests__/__snapshots__/button.test.tsx.snap b/packages/paste-core/components/button/__tests__/__snapshots__/button.test.tsx.snap
index 3424810513..661d376263 100644
--- a/packages/paste-core/components/button/__tests__/__snapshots__/button.test.tsx.snap
+++ b/packages/paste-core/components/button/__tests__/__snapshots__/button.test.tsx.snap
@@ -588,7 +588,7 @@ exports[`Button States Has a loading state 1`] = `
- Loading Icon
+ Loading, please wait.
- Loading Icon
+ Loading, please wait.
- Loading Icon
+ Loading, please wait.
- Loading Icon
+ Loading, please wait.
- Loading Icon
+ Loading, please wait.
- Loading Icon
+ Loading, please wait.
= props => {
{props.children}
{showLoading ? (
-
+
) : null}
diff --git a/packages/paste-core/components/button/stories/index.stories.tsx b/packages/paste-core/components/button/stories/index.stories.tsx
index dddcb143fe..576014d121 100644
--- a/packages/paste-core/components/button/stories/index.stories.tsx
+++ b/packages/paste-core/components/button/stories/index.stories.tsx
@@ -50,7 +50,7 @@ storiesOf('Components|Button', module)
onFocus={action('handleFocus')}
onBlur={action('handleBlur')}
>
-
+ Activate
);
@@ -71,7 +71,7 @@ storiesOf('Components|Button', module)
onFocus={action('handleFocus')}
onBlur={action('handleBlur')}
>
-
+
);
})
@@ -105,13 +105,13 @@ storiesOf('Components|Button', module)
@@ -153,13 +153,13 @@ storiesOf('Components|Button', module)
@@ -201,13 +201,13 @@ storiesOf('Components|Button', module)
@@ -249,13 +249,13 @@ storiesOf('Components|Button', module)
@@ -297,13 +297,13 @@ storiesOf('Components|Button', module)
diff --git a/packages/paste-core/components/spinner/src/index.tsx b/packages/paste-core/components/spinner/src/index.tsx
index 78bc206d53..1e26c57d70 100644
--- a/packages/paste-core/components/spinner/src/index.tsx
+++ b/packages/paste-core/components/spinner/src/index.tsx
@@ -34,15 +34,14 @@ interface SpinnerProps extends LoadingIconProps {
size?: IconSize;
}
-const Spinner: React.FC = ({as, size, iconColor, decorative}) => (
+const Spinner: React.FC = ({as, size, iconColor, decorative, title}) => (
-
+
);
Spinner.defaultProps = {
size: 'sizeIcon20',
- decorative: false,
};
Spinner.displayName = 'Spinner';
diff --git a/packages/paste-core/components/spinner/stories/index.stories.tsx b/packages/paste-core/components/spinner/stories/index.stories.tsx
index e651ebc22d..9f9d45f1f4 100644
--- a/packages/paste-core/components/spinner/stories/index.stories.tsx
+++ b/packages/paste-core/components/spinner/stories/index.stories.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
-import {withKnobs, text, select} from '@storybook/addon-knobs';
+import {withKnobs, text, select, boolean} from '@storybook/addon-knobs';
import {TextColor, IconSize} from '@twilio-paste/types';
import {DefaultTheme} from '@twilio-paste/theme-tokens';
import {Spinner} from '../src';
@@ -13,8 +13,16 @@ storiesOf('Components|Spinner', module)
.add('Default', () => {
const iconColorValue = select('iconColor', IconColorOptions, 'currentColor') as TextColor;
const sizeValue = select('size', SizeOptions, 'sizeIcon20') as IconSize;
+ const decorativeValue = boolean('decorative', false);
- return ;
+ return (
+
+ );
})
.add('Responsive', () => {
return (
@@ -22,6 +30,7 @@ storiesOf('Components|Spinner', module)
iconColor={['colorText', 'colorTextError']}
size={['sizeIcon10', 'sizeIcon20', 'sizeIcon30', 'sizeIcon40']}
title={text('title', 'Now loading')}
+ decorative={false}
/>
);
});
diff --git a/packages/paste-icons/README.md b/packages/paste-icons/README.md
index 7cd443b9dc..6b3112640d 100644
--- a/packages/paste-icons/README.md
+++ b/packages/paste-icons/README.md
@@ -19,12 +19,12 @@ import AssetsIcon from '@twilio-paste/icons/react/AssetsIcon';
### Standard Props
-| Prop | Type | Description | Default |
-| ----------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
-| decorative? | boolean | Whether or not the SVG is just visual flair or adds meaning to the page. Specifically for screenreaders to know whether to read out the title or not. | true |
-| title? | string | The accesibility text that is read when screenreaders get to this component | Component name |
-| size? | number | The width and height value (all icons are square) in pixels | 24 |
-| color? | string | The color of your icon | currentColor - whatever is the font-color inherited from up the DOM tree |
+| Prop | Type | Description | Default |
+| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
+| decorative | boolean | Whether or not the SVG is just visual flair or adds meaning to the page. Specifically for screenreaders to know whether to read out the title or not. | true |
+| title? | string | The accesibility text that is read when screenreaders get to this component | Component name |
+| size? | number | The width and height value (all icons are square) in pixels | 24 |
+| color? | string | The color of your icon | currentColor - whatever is the font-color inherited from up the DOM tree |
Keep in mind these props are the **Base Guarantee** for icon components. Some icons may have additional functionality as needed (a way to style two color options for example).
diff --git a/packages/paste-icons/src/LoadingIcon.tsx b/packages/paste-icons/src/LoadingIcon.tsx
index e8be014609..a591275fb7 100644
--- a/packages/paste-icons/src/LoadingIcon.tsx
+++ b/packages/paste-icons/src/LoadingIcon.tsx
@@ -7,29 +7,30 @@ import {IconWrapper, IconWrapperProps} from './helpers/IconWrapper';
export interface LoadingIconProps extends IconWrapperProps {
title?: string;
- decorative?: boolean;
+ decorative: boolean;
}
-const LoadingIcon: React.FC = ({title, decorative, ...props}) => (
-
-
- {uid => (
-
- )}
-
-
-);
+const LoadingIcon: React.FC = ({as, size, iconColor, title, decorative}) => {
+ if (!decorative && title == null) {
+ throw new Error('[LoadingIcon]: Missing a title for non-decorative icon.');
+ }
-LoadingIcon.defaultProps = {
- title: 'Loading Icon',
- decorative: true,
+ return (
+
+
+ {uid => (
+
+ )}
+
+
+ );
};
LoadingIcon.displayName = 'LoadingIcon';
diff --git a/packages/paste-icons/src/PlusIcon.tsx b/packages/paste-icons/src/PlusIcon.tsx
index 9e15bf7f86..cbb0ed94e6 100644
--- a/packages/paste-icons/src/PlusIcon.tsx
+++ b/packages/paste-icons/src/PlusIcon.tsx
@@ -7,29 +7,30 @@ import {IconWrapper, IconWrapperProps} from './helpers/IconWrapper';
export interface PlusIconProps extends IconWrapperProps {
title?: string;
- decorative?: boolean;
+ decorative: boolean;
}
-const PlusIcon: React.FC = ({title, decorative, ...props}) => (
-
-
- {uid => (
-
- )}
-
-
-);
+const PlusIcon: React.FC = ({as, size, iconColor, title, decorative}) => {
+ if (!decorative && title == null) {
+ throw new Error('[PlusIcon]: Missing a title for non-decorative icon.');
+ }
-PlusIcon.defaultProps = {
- title: 'Plus Icon',
- decorative: true,
+ return (
+
+
+ {uid => (
+
+ )}
+
+
+ );
};
PlusIcon.displayName = 'PlusIcon';
diff --git a/packages/paste-icons/tools/templates/reactIconTemplate.js b/packages/paste-icons/tools/templates/reactIconTemplate.js
index 96bf859188..cc1a067072 100644
--- a/packages/paste-icons/tools/templates/reactIconTemplate.js
+++ b/packages/paste-icons/tools/templates/reactIconTemplate.js
@@ -1,6 +1,4 @@
// Note on a11y: https://css-tricks.com/can-make-icon-system-accessible/
-const {pascalCaseWordSplitter} = require('../utils');
-
const reactIconTemplate = ({componentName, svg}) => `
/**
* This file was automatically generated with @twilio-labs/svg-to-react
@@ -11,23 +9,25 @@ import {IconWrapper, IconWrapperProps} from './helpers/IconWrapper';
export interface ${componentName}Props extends IconWrapperProps {
title?: string;
- decorative?: boolean;
+ decorative: boolean;
}
-const ${componentName}: React.FC<${componentName}Props> = ({title, decorative, ...props}) => (
-
-
- {uid => (
- ${svg}
- )}
-
-
-);
+const ${componentName}: React.FC<${componentName}Props> = ({as, size, iconColor, title, decorative}) => {
+ if (!decorative && title == null) {
+ throw new Error('[${componentName}]: Missing a title for non-decorative icon.');
+ }
-${componentName}.defaultProps = {
- title: '${pascalCaseWordSplitter(componentName)}',
- decorative: true,
+ return (
+
+
+ {uid => (
+ ${svg}
+ )}
+
+
+ );
}
+
${componentName}.displayName = '${componentName}';
export {${componentName}};
diff --git a/packages/paste-icons/tools/templates/storybookListTemplate.js b/packages/paste-icons/tools/templates/storybookListTemplate.js
index 90ad1e4992..eef388b6f6 100644
--- a/packages/paste-icons/tools/templates/storybookListTemplate.js
+++ b/packages/paste-icons/tools/templates/storybookListTemplate.js
@@ -22,7 +22,7 @@ ${importIconList}
interface IconProps {
title?: string;
- decorative?: boolean;
+ decorative: boolean;
size?: number;
color?: string;
}
diff --git a/packages/paste-website/package.json b/packages/paste-website/package.json
index de70dc751f..b47bf67008 100644
--- a/packages/paste-website/package.json
+++ b/packages/paste-website/package.json
@@ -22,6 +22,7 @@
"@mdx-js/react": "^1.0.23",
"@mdx-js/tag": "^0.20.3",
"@styled-system/theme-get": "^5.1.2",
+ "@twilio-paste/absolute": "^2.0.2",
"@twilio-paste/anchor": "^1.0.2",
"@twilio-paste/aspect-ratio": "^1.0.2",
"@twilio-paste/box": "^2.0.2",
@@ -33,7 +34,6 @@
"@twilio-paste/theme": "^2.0.2",
"@twilio-paste/theme-tokens": "^2.0.2",
"@twilio-paste/typography": "^1.0.2",
- "@types/react-scrollspy": "^3.3.1",
"color": "^3.1.2",
"gatsby": "^2.13.4",
"gatsby-image": "^2.2.8",
@@ -66,7 +66,9 @@
"@types/color": "^3.0.0",
"@types/jest": "^24.0.18",
"@types/lodash": "^4.14.136",
- "@types/react-helmet": "^5.0.9",
+ "@types/react": "^16.9.11",
+ "@types/react-helmet": "^5.0.14",
+ "@types/react-scrollspy": "^3.3.1",
"@types/react-test-renderer": "^16.9.0",
"babel-preset-gatsby": "^0.2.8",
"prettier": "^1.18.2",
diff --git a/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-1.png b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-1.png
new file mode 100644
index 0000000000..c921c85001
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-1.png differ
diff --git a/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-2.png b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-2.png
new file mode 100644
index 0000000000..80ec3116d8
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-2.png differ
diff --git a/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-3.png b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-3.png
new file mode 100644
index 0000000000..beb47f9dff
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/formatting-an-icon-3.png differ
diff --git a/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-1.png b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-1.png
new file mode 100644
index 0000000000..4c4d1d472e
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-1.png differ
diff --git a/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-2.png b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-2.png
new file mode 100644
index 0000000000..8888392024
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-2.png differ
diff --git a/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-3.png b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-3.png
new file mode 100644
index 0000000000..4a4f9550e8
Binary files /dev/null and b/packages/paste-website/src/assets/images/icon-guide/using-streamline-icons-3.png differ
diff --git a/packages/paste-website/src/components/paste-mdx-provider/index.tsx b/packages/paste-website/src/components/paste-mdx-provider/index.tsx
index e266088463..9dbef39a13 100644
--- a/packages/paste-website/src/components/paste-mdx-provider/index.tsx
+++ b/packages/paste-website/src/components/paste-mdx-provider/index.tsx
@@ -26,6 +26,13 @@ const StyledContent = styled.div`
max-width: 816px;
`;
+// These sub styles fix a bug that causes it to break line-heights
+const StyledSup = styled.sup`
+ vertical-align: baseline;
+ position: relative;
+ top: -0.4rem;
+`;
+
const shortcodes = {ComponentHeader, LivePreview, TableOfContents};
/* eslint-disable no-shadow */
@@ -66,6 +73,7 @@ export const PasteMDXProvider: React.FC = (props: PasteMD
hr: (props: React.ComponentProps<'hr'>): React.ReactElement => ,
a: (props: Anchor): React.ReactElement => , // eslint-disable-line jsx-a11y/anchor-has-content
img: (props: React.ComponentProps<'img'>): React.ReactElement => , // eslint-disable-line jsx-a11y/alt-text
+ sup: (props: React.ComponentProps<'sup'>): React.ReactElement => ,
content: (props: React.ComponentProps<'div'>): React.ReactElement => ,
contentwrapper: (props: React.ComponentProps<'div'>): React.ReactElement => ,
}}
diff --git a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.styles.ts b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.styles.ts
index 277f4faab9..d4c9c3cd7f 100644
--- a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.styles.ts
+++ b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.styles.ts
@@ -24,6 +24,7 @@ export const SiteNavNestList: React.FC = styled(SiteNavLis
overflow: hidden;
border-bottom-right-radius: ${themeGet('radii.borderRadius20')};
border-bottom-left-radius: ${themeGet('radii.borderRadius20')};
+ padding-left: ${themeGet('space.space40')};
`;
export const SiteNavItem = styled.li`
diff --git a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
index 1eee8a2d62..b8d4cf513a 100644
--- a/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
+++ b/packages/paste-website/src/components/site-wrapper/sidebar/SidebarNavigation.tsx
@@ -78,15 +78,18 @@ const SidebarNavigation: React.FC = () => {
const data: SiteWrapperPageQuery = useStaticQuery(pageQuery);
const [componentsOpen, setComponentsOpen] = React.useState(
- getCurrentPathname().includes(SidebarCategoryRoutes.COMPONENTS)
+ getCurrentPathname().startsWith(SidebarCategoryRoutes.COMPONENTS)
);
- const [utilitiesOpen, setutilitiesOpen] = React.useState(
- getCurrentPathname().includes(SidebarCategoryRoutes.UTILITIES)
+ const [utilitiesOpen, setUtilitiesOpen] = React.useState(
+ getCurrentPathname().startsWith(SidebarCategoryRoutes.UTILITIES)
+ );
+ const [iconSystemOpen, setIconSystemOpen] = React.useState(
+ getCurrentPathname().startsWith(SidebarCategoryRoutes.ICON_SYSTEM)
);
const [gettingStartedOpen, setgettingStartedOpen] = React.useState(
- getCurrentPathname().includes(SidebarCategoryRoutes.GETTING_STARTED)
+ getCurrentPathname().startsWith(SidebarCategoryRoutes.GETTING_STARTED)
);
- const [tokensOpen, setTokensOpen] = React.useState(getCurrentPathname().includes(SidebarCategoryRoutes.TOKENS));
+ const [tokensOpen, setTokensOpen] = React.useState(getCurrentPathname().startsWith(SidebarCategoryRoutes.TOKENS));
return (
@@ -170,7 +173,7 @@ const SidebarNavigation: React.FC = () => {
setutilitiesOpen(!utilitiesOpen)}
+ onClick={() => setUtilitiesOpen(!utilitiesOpen)}
isOpen={utilitiesOpen}
aria-expanded={utilitiesOpen}
>
@@ -192,6 +195,26 @@ const SidebarNavigation: React.FC = () => {
})}
+
+ setIconSystemOpen(!iconSystemOpen)}
+ isOpen={iconSystemOpen}
+ aria-expanded={iconSystemOpen}
+ >
+ Icon System
+
+
+
+
+ Usage Guidelines
+
+
+
+ How to add an icon
+
+
+
+ Migration Guide
diff --git a/packages/paste-website/src/constants.ts b/packages/paste-website/src/constants.ts
index fd81cee3aa..f20442c731 100644
--- a/packages/paste-website/src/constants.ts
+++ b/packages/paste-website/src/constants.ts
@@ -7,6 +7,7 @@ export const SidebarCategoryRoutes = {
COMPONENTS: '/components',
PRIMITIVES: '/primitives',
UTILITIES: '/utilities',
+ ICON_SYSTEM: '/icon-system',
GETTING_STARTED: '/getting-started',
TOKENS: '/tokens',
};
diff --git a/packages/paste-website/src/pages/icon-system/how-to-add-an-icon.mdx b/packages/paste-website/src/pages/icon-system/how-to-add-an-icon.mdx
new file mode 100644
index 0000000000..437539e11e
--- /dev/null
+++ b/packages/paste-website/src/pages/icon-system/how-to-add-an-icon.mdx
@@ -0,0 +1,134 @@
+---
+title: How to add an icon
+description: Guidelines on how to create and export SVG icons for Paste.
+---
+
+import {graphql} from 'gatsby';
+import {Link} from 'gatsby';
+import Img from 'gatsby-image';
+import {Box} from '@twilio-paste/box';
+import {Text} from '@twilio-paste/text';
+import {Button} from '@twilio-paste/button';
+import {PlusIcon} from '@twilio-paste/icons/esm/PlusIcon';
+import {LoadingIcon} from '@twilio-paste/icons/esm/LoadingIcon';
+import {Callout, CalloutTitle, CalloutText} from '../../components/callout';
+import {Grid} from '../../components/grid';
+
+export const pageQuery = graphql`
+ {
+ mdx(fields: {slug: {eq: "/icon-system/how-to-add-an-icon/"}}) {
+ headings {
+ depth
+ value
+ }
+ }
+ }
+`;
+
+
+
+
+
+
{props.pageContext.frontmatter.title}
+Written for Sketch 59.1
+
+---
+
+## Finding an icon
+
+1. Make sure the icon you need doesn’t already live in the [Paste repo](https://twilio-labs.github.io/paste/?path=/story/overview-icons--list)
+and that you can’t use one of the existing ones.
+2. Find a new icon by searching through the [Streamline app](https://app.streamlineicons.com/streamline-regular),
+then message #help-design-system for the Sketch file.
+3. Create a branch in Abstract.
+ 1. If you’re planning on adding the icon to Paste, first [create a Github issue](https://github.com/twilio-labs/paste/issues).
+ Explain what you’re using it for, and list other potential use cases outside of your own.
+ Add a link or screenshot of the proposed icon. If the team agrees, create a branch in
+ the ["Paste icons" Abstract project](https://share.goabstract.com/0bdb9ae0-d1da-4021-80d3-338683d27b42).
+ 2. If the icon won’t be added to Paste, create a branch in your own Abstract project
+ and skip to [Using a Streamline icon](/icon-system/how-to-add-an-icon#using-a-streamline-icon).
+
+
+## Setting up an icon in Paste Icons
+
+1. Open "paste icons.sketch".
+2. Go to the page "Symbols".
+3. Duplicate the empty template artboards ("[16/icon name](https://share.goabstract.com/26080d38-a95a-46b1-a0ad-aa4ddba4abb3)",
+"[20/icon name](https://share.goabstract.com/96294d4e-50cc-4c7e-9cb9-b82a045fe970)", and "[24/icon name](https://share.goabstract.com/f51d0d18-67ce-4c02-8c44-5f8d390863d7)").
+Make sure the artboards are aligned evenly on the canvas with the existing icon artboards.
+4. Turn the new artboards into symbols.
+5. In the artboard names, replace "icon name" with the name of your icon.
+
+
+## Using a Streamline icon
+
+1. Copy and paste the Streamline icon onto the 24px artboard.
+2. Select all the layers and change the border width to 2.
+![Example of border width set to 2](../../assets/images/icon-guide/using-streamline-icons-1.png)
+3. Select the layer group. Lock the width-height aspect ratio. If the icon appears
+larger than the bounds of the artboard, shrink the icon down to just fill the
+artboard. 20–22 px usually works.
+![Example of locked width and height values](../../assets/images/icon-guide/using-streamline-icons-2.png)
+4. Duplicate the layer group. Rename the original group to "source" and hide it.
+5. Ungroup the new layer group. You’ll be working with these layers.
+![Sample layer groups](../../assets/images/icon-guide/using-streamline-icons-3.png)
+
+
+## Formatting an icon
+
+1. Convert your icons to outlines so that the width of the lines stay consistent.
+ 1. Select all the layers and press "SHIFT + COMMAND + O", or use Sketch's menu, "Layer > Convert to Outlines".
+ 2. Make sure each layer now has a fill and not a stroke.
+2. Create a union with all the pieces of your icon. This will merge individual pieces
+together to create one shape. You can find the Union tool in Sketch's toolbar at the
+top or go to "Layer > Combine > Union".
+3. Apply the layer style "icon/color-text-icon" from one of the Paste themes to the
+combined shape. In the layer list, you should only have this shape and the hidden
+"source" group.
+4. Rename the combined shape to "[icon name]".
+5. Make sure the icon is centered on the artboard both vertically and horizontally.
+If you’re working with an asymmetric shape, you might need to adjust it manually to make
+sure it’s [optically centered](https://blog.marvelapp.com/optical-adjustment-logic-vs-designers/).
+For example, you might need to move a "play" icon a bit
+right-of-center to account for the low visual weight on its right side.
+6. Make sure there are no transforms on the shape. If there are, go to "Layer > Combine > Flatten".
+![Number of transforms preview](../../assets/images/icon-guide/formatting-an-icon-1.png)
+7. This is what your layer and sidebar should look like:
+![Example of what Sketch should look like](../../assets/images/icon-guide/formatting-an-icon-2.png)
+8. If you’re working on a Paste icon, place the "24/[icon name]" symbol on the
+"20/[icon name]" and "16/[icon name]" artboards. Resize them to 20px × 20px and
+16px × 16px respectively. Fix the size of the layer horizontally and vertically.
+![Example of how to size icons](../../assets/images/icon-guide/formatting-an-icon-3.png)
+9. Rename the layer to "icon".
+
+
+## Exporting and adding the icon
+
+1. Make sure you have the [SVGO compressor Sketch plugin](https://www.sketch.com/extensions/plugins/svgo-compressor/) installed.
+2. Export the 24px icon artboard (not the combined shape) as an SVG. The template artboard
+is already set up to do this.
+3. Make sure the SVG code is clean.
+ 1. You shouldn’t see `id`. If you do, make sure the SVGO compressor plugin is properly installed.
+ 2. You shouldn’t see `transform`. If you do, flatten your shape.
+ 3. You should see `width=”24" height="24"`. If you don’t, check that the artboard you’re
+ exporting is 24px × 24px and you’re exporting the artboard, not the layer.
+4. Commit your changes in Abstract and request a review from the Paste design team.
+5. Add the SVG to your code.
+ 1. If the icon will be added to Paste, attach the SVG file to the original Github issue.
+ The Paste team will add it to the repo and let you know when it’s done.
+ 2. If it won’t be added to Paste, use the [svg-to-react](https://github.com/twilio-labs/svg-to-react)
+ utility to create an accessible and extendable React component.
+
+
+## Troubleshooting
+
+If you run into any issues with creating your icon, double check you've followed
+all the steps. In the past, we've seen icons that had small artifacts or
+missing pieces because of the order of layers within a combined shape
+or union. Try moving layers around to get the shape you need.
+
+Get in touch with the Paste team through Slack at #help-design-system.
+
+
+
+
diff --git a/packages/paste-website/src/pages/icon-system/index.mdx b/packages/paste-website/src/pages/icon-system/index.mdx
new file mode 100644
index 0000000000..e37a41afcc
--- /dev/null
+++ b/packages/paste-website/src/pages/icon-system/index.mdx
@@ -0,0 +1,238 @@
+---
+title: Icon Usage Guidelines
+description: Overview on how to use icons in the context of Paste projects.
+---
+
+import {graphql} from 'gatsby';
+import {Link} from 'gatsby';
+import {Box} from '@twilio-paste/box';
+import {Text} from '@twilio-paste/text';
+import {Absolute} from '@twilio-paste/absolute';
+import {Anchor} from '@twilio-paste/anchor';
+import {Button} from '@twilio-paste/button';
+import {PlusIcon} from '@twilio-paste/icons/esm/PlusIcon';
+import {LoadingIcon} from '@twilio-paste/icons/esm/LoadingIcon';
+import {Callout, CalloutTitle, CalloutText} from '../../components/callout';
+import {DoDont, Do, Dont} from '../../components/DoDont';
+import Changelog from '@twilio-paste/icons/CHANGELOG.md';
+
+export const pageQuery = graphql`
+ {
+ mdx(fields: {slug: {eq: "/icon-system/"}}) {
+ headings {
+ depth
+ value
+ }
+ }
+ }
+`;
+
+
+
+
+
+
+
+
+
{props.pageContext.frontmatter.title}
+
+
+
+ You can view and play with icons on our storybook icon list.
+ Our icon set is still limited, and more will be added over time. Please{' '}
+ file an icon request or
+ follow the guide on how to add an icon.
+
+
+
+
+Icons are small graphical representation of a program or a function. They can be used to enhance the
+appearance of a web interface and break up the monotony of text.
+
+Use an icon only when it provides additional clarity or is necessary to draw attention to a UI element.
+If an icon is used to indicate hierarchy of a UI element, all elements at the same hierarchy should also be paired with an icon.
+
+
+## Accessibility
+
+### Specify if an icon is decorative
+
+Icons can either be **decorative** or **informative**. *"Decorative icons don’t add information to the content
+of a page. The information provided by the image might already be given using adjacent text, or the image
+might be included to make the website more visually attractive."* (w3.org)
+
+Our icons require the developer define whether an icon is decorative by [providing the required property](/icon-system#making-an-icon-decorative-or-informative).
+
+### Give informative icons a title
+
+A title is required so assistive technology can infer equal meaning as a sighted user would.
+
+
+
+
+ (Hover over icon for tooltip)
+
+
+
+ (Hover over icon for tooltip)
+
+
+
+
+### Pair icons with text
+
+Remember that different cultures may interpret the same image in different ways so despite our best intentions it is helpful to provide adjoining text.
+
+
+
+ We're working on a better example for this section. #TODO
+
+
+
+
+
+
+ Add to cart
+
+
+
+
+
+
+
+### Icons shouldn't be interactive
+
+Icons should not have interactions such as click or hover behavior. Instead they should be wrapped with an
+interactive element such as [Button](/components/button) and [Anchor](/components/anchor).
+
+
+
+
+
+
+
{alert(`This isn't a great user experience.`)}}>
+
+
+
+
+
+
+## Examples
+
+#### Adapting the icon's color
+
+We can change the icon color directly using [text color tokens](/tokens/#text-colors).
+
+
+ {``}
+
+
+Alternatively, icons can inherit the current text color.
+
+
+ {`
+
+
+ Now loading
+
+`}
+
+
+
+#### Resizing icons
+
+Icons accept one of our `sizeIcon` [tokens](/tokens/#sizings).
+
+
+ {`
+
+
+
+
+`}
+
+
+
+#### Making an icon decorative or informative
+
+If an icon is decorative, no title is necessary. However, if an icon isn't decorative,
+you must provide a title property.
+
+
+ {`
+
+
+`}
+
+
+## Usage Guide
+
+### Installation
+
+```bash
+yarn add @twilio-paste/icons
+```
+
+### Usage
+
+Icons have to be imported individually. This import style was decided for [important
+performance benefits](/icon-system/#why-react-components). As our icon set grows to contain over 100 icons, we don't
+want projects that use only a few icons to pay the price of a large bundle.
+
+```jsx
+import {PlusIcon} from '@twilio-paste/icons/esm/PlusIcon';
+
+
+```
+
+### Props
+
+| Prop | Type | Description | Default |
+| ---------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
+| as? | string | The HTML tag to replace the default `
+