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 => ( - - {title ? {title} : null} - - - )} - - -); +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 => ( + + {title ? {title} : null} + + + )} + + + ); }; 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 => ( - - {title ? {title} : null} - - - )} - - -); +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 => ( + + {title ? {title} : null} + + + )} + + + ); }; 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 `