diff --git a/cypress/integration/sitemap-vrt/constants.ts b/cypress/integration/sitemap-vrt/constants.ts index 76217bf1a9..da395e352e 100644 --- a/cypress/integration/sitemap-vrt/constants.ts +++ b/cypress/integration/sitemap-vrt/constants.ts @@ -290,6 +290,7 @@ export const SITEMAP = [ "/introduction/about-paste/", "/introduction/contributing/components/", "/foundations/content/", + "/foundations/elevation/", "/introduction/contributing/icons/", "/introduction/contributing/patterns/", "/introduction/for-designers/design-guidelines/", diff --git a/packages/paste-website/src/assets/images/foundations/elevation/elevated-background-color-tokens.png b/packages/paste-website/src/assets/images/foundations/elevation/elevated-background-color-tokens.png new file mode 100644 index 0000000000..1874cf926c Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/elevated-background-color-tokens.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-1-accent-shadow.png b/packages/paste-website/src/assets/images/foundations/elevation/level-1-accent-shadow.png new file mode 100644 index 0000000000..f9280814d0 Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-1-accent-shadow.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-1-tonal-change.png b/packages/paste-website/src/assets/images/foundations/elevation/level-1-tonal-change.png new file mode 100644 index 0000000000..1a7f7b37bb Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-1-tonal-change.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-2-accent-shadow.png b/packages/paste-website/src/assets/images/foundations/elevation/level-2-accent-shadow.png new file mode 100644 index 0000000000..2e73fc7d1b Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-2-accent-shadow.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-2-risen-shadow.png b/packages/paste-website/src/assets/images/foundations/elevation/level-2-risen-shadow.png new file mode 100644 index 0000000000..6b7c176543 Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-2-risen-shadow.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-3-overlay.png b/packages/paste-website/src/assets/images/foundations/elevation/level-3-overlay.png new file mode 100644 index 0000000000..4a151bee60 Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-3-overlay.png differ diff --git a/packages/paste-website/src/assets/images/foundations/elevation/level-3-risen-shadow.png b/packages/paste-website/src/assets/images/foundations/elevation/level-3-risen-shadow.png new file mode 100644 index 0000000000..c8b3a7becb Binary files /dev/null and b/packages/paste-website/src/assets/images/foundations/elevation/level-3-risen-shadow.png differ diff --git a/packages/paste-website/src/component-examples/ElevationFoundationExamples.tsx b/packages/paste-website/src/component-examples/ElevationFoundationExamples.tsx new file mode 100644 index 0000000000..9bcb87c9d3 --- /dev/null +++ b/packages/paste-website/src/component-examples/ElevationFoundationExamples.tsx @@ -0,0 +1,355 @@ +import { ImageCaption } from "../components/ImageCaption"; + +export const ElevatedBackgroundColorTokensExample = ` +const ElevatedBackgroundColorTokens = () => { + const TokensTemplate = ({ + backgroundColor, + backgroundColorElevated, + hasBorder, + backgroundColorBase, + backgroundColorElevation, + }) => { + return ( + + + + + + + + Base: + + + {backgroundColorBase} + + + Elevation: + + + {backgroundColorElevation} + + + + ); + }; + return ( + + + + + + + ); +}; + +render( + +) +`.trim(); + +export const ShadowTokensLevel1Example = ` +const ShadowTokensLevel1 = () => { + return ( + + + + + + + Selected theme + + + + $shadow-elevation-05 + + + + + + + + + + Dark mode + + + + $shadow-elevation-05 + + + + + + ); +}; + +render( + +) +`.trim(); + +export const ShadowTokensLevel2Example = ` +const ShadowTokensLevel2 = () => { + return ( + + + + + + + Selected theme + + + + $shadow-elevation-10 + + + + + + + + + + Dark mode + + + + $shadow-elevation-10 + + + + + + ); +}; + +render( + +) +`.trim(); + +export const ShadowTokensLevel3Example = ` +const ShadowTokensLevel3 = () => { + return ( + + + + + + + Selected theme + + + + $shadow-elevation-20 + + + + + + + + + + Dark mode + + + + $shadow-elevation-20 + + + + + + ); +}; + +render( + +) +`.trim(); + +export const ApplyingLevel1ImgCaption = (): JSX.Element => ( + + For example: The Side Panel pushes content at Level 0 but is categorized as Level 1, and uses $shadow-elevation-05 + because it contains a task that requires user focus while still relying on the background content. + +); + +export const CalloutExampleImgCaption = (): JSX.Element => ( + + For example: a Callout is typically placed inline with the background content and relies on that context for its + meaning. However, it uses a slight background change with $color-background-body-elevation to subtly appear more + prominent and make its information stand out on top of the default background body color. + +); + +export const ApplyLevel2ImgCaption = (): JSX.Element => ( + + For example: a Topbar's role is not to be at a high hierarchy but to have enough emphasis and differentiation + to be easily identified at any point of the experience. + +); + +export const PopoverExampleImgCaption = (): JSX.Element => ( + + For example: a Popover uses a subtle, sharp shadow with $shadow-elevation-10 to create the illusion of floating + above the content, while still being bound to its trigger button. This adds depth without dominating the + interaction. + +); diff --git a/packages/paste-website/src/pages/foundations/elevation/index.mdx b/packages/paste-website/src/pages/foundations/elevation/index.mdx new file mode 100644 index 0000000000..e565173c83 --- /dev/null +++ b/packages/paste-website/src/pages/foundations/elevation/index.mdx @@ -0,0 +1,233 @@ +export const meta = { + title: 'Elevation', + description: 'Elevation organizes elements along a visual hierarchy creating a sense of depth in an interface.', + slug: '/foundations/elevation/', +}; + +import {Anchor} from '@twilio-paste/anchor'; +import {PageHeaderSeparator} from '@twilio-paste/page-header' +import {Separator} from '@twilio-paste/separator' +import {Callout, CalloutHeading, CalloutText} from '@twilio-paste/callout'; +import {UnorderedList, ListItem} from '@twilio-paste/list'; +import {Box} from '@twilio-paste/box'; +import {Text} from '@twilio-paste/text'; +import {Theme} from '@twilio-paste/theme'; + +import {ResponsiveImage} from '../../../components/ResponsiveImage'; +import {DoDont, Do, Dont} from '../../../components/DoDont'; +import {SidebarCategoryRoutes} from '../../../constants'; +import AccentShadow from '../../../assets/images/foundations/elevation/level-1-accent-shadow.png'; +import TonalChange from '../../../assets/images/foundations/elevation/level-1-tonal-change.png'; +import Level2AccentChange from '../../../assets/images/foundations/elevation/level-2-accent-shadow.png'; +import Level2RisenShadow from '../../../assets/images/foundations/elevation/level-2-risen-shadow.png'; +import Level3Overlay from '../../../assets/images/foundations/elevation/level-3-overlay.png'; +import Level3RisenShadow from '../../../assets/images/foundations/elevation/level-3-risen-shadow.png'; +import ElevatedBackgroundTokens from '../../../assets/images/foundations/elevation/elevated-background-color-tokens.png'; + +import { + ApplyingLevel1ImgCaption, + CalloutExampleImgCaption, + ApplyLevel2ImgCaption, + PopoverExampleImgCaption, +} from '../../../component-examples/ElevationFoundationExamples'; +import DefaultLayout from '../../../layouts/DefaultLayout'; +import {getNavigationData} from '../../../utils/api'; +import { ElevatedBackgroundColorTokensExample, ShadowTokensLevel1Example, ShadowTokensLevel2Example, ShadowTokensLevel3Example } from '../../../component-examples/ElevationFoundationExamples'; +import { ThemeIcon } from '@twilio-paste/icons/esm/ThemeIcon'; +import { DarkModeIcon } from '@twilio-paste/icons/esm/DarkModeIcon'; + +export default DefaultLayout; + +export const getStaticProps = async () => { + const navigationData = await getNavigationData(); + return { + props: { + navigationData, + }, + }; +}; + + + + + + + + + + + + + + + +## Introduction + +Elevation represents how "close" or "far" an element feels from the user in a digital context, using a combination of **shadows**, **colors** and **overlays** to create a layering effect, making elements feel distinct, focused or prioritized. + +Paste’s elevation system is: + +- Subtle: Provides just enough visual contrast to create a sense of depth, avoiding excessive visual clutter. +- Intentional: Provides clear meaning to guide users through interfaces, improving user efficiency by consistently communicating the level of importance of elements across different pages. +- Dynamic: Adapts to different background colors and themes, ensuring consistent depth perception and usability across light, dark, and custom design contexts. + +## Elevation levels + +Paste has a total of 4 levels of elevation. Each elevation level corresponds to a perceived distance within the interface. All components are assigned a level based on their intended distance against the surface behind them. + +### Level 0 + +Elements at this level are the baseline for all the other levels and represent the background of the interface. + +The majority of elements will be at this level in their resting states. However an element on this level can move to a higher level when interacted with. + +For example: a Button might be ‘Level 0’ in its resting state, but ‘Level 1’ when hovered. + +### Level 1 + +Elements at this level require subtle differentiation from the surface behind them without adding too much distance. These elements are either presented by default or triggered within the composition but typically do not open on top of other elements. + +There are 2 different ways to communicate an element's elevation in level 1: + +1. **Use an accent shadow** for elements that need to be distinct from the surrounding elements but not highlighted. + + + + + +2. **Use a tonal difference** when elements need to be highlighted as well as distinct from the background. + + + + + +### Level 2 + +Elements that rest at this level are distinctly separated from the background and dynamically overlap content layers creating visible distance while still being bound to Level 0 elements. + +There are 2 different ways to communicate an element's elevation in level 2: + +1. **Use a tonal difference with an accent shadow** when an element needs to be highlighted and prominent but does not overlap content by default. **Combine this with a soft sharp shadow** when the element overlaps other content and extra distance is necessary to make it distinct from the background. + + + + + +2. **Use a soft and sharp shadow** for elements that have temporary prominence or subtly float above the content. + +Interactive states like vertical or horizontal scroll can dynamically use a soft, sharp shadow to indicate that there is overflown content. + + + + + +### Level 3 + +Elements that rest at this level float above all other levels, and tend to contain crucial actions or information that temporarily moves to the top hierarchy of the surface. + +1. **Use a large, soft shadow**, for elements that are at the top priority and require user focus, but still allow the user to interact with the surrounding content. + +For elements that already have their own border, use `$shadow-elevation-20`. Otherwise, use `$shadow-elevation-top-20` to define its edges against the background. + + + + +2. **Use a large soft shadow along with an overlay** for elements that completely isolate the interface to draw full focus from the user. + + + +## Elevated background color tokens + +Subtle tonal shifts are an effective way to communicate hierarchy and prominence for elevated elements without adding visual clutter to the screen. + +Paste's elevated background color tokens are derived from a base background color with tonal shifts to indicate elevation. Elevated surfaces darken on light backgrounds and lighten on dark backgrounds, ensuring clarity and consistency across themes. + +View our full list of Elevated background color tokens +

+ +For levels 1 and 2, when showing elevation through tonal differences, pair elevated background color tokens with their corresponding background color. For example: Use `$color-background-body-elevation` on top of `$color-background-body`. + + + {ElevatedBackgroundColorTokensExample} + + +Elevated elements across levels that share the same background color as the main page body should use the token `$color-background-weaker`. This token matches the page body tone in light mode but appears slightly lighter in dark mode to subtly reinforce elevation. + + + + + Different uses of color + + + Elevation Colors: Subtle tones for prominence and hierarchy, used to separate elements visually from the background. + Semantic Colors: Used to communicate meaning or status, such as success, warning, or error. + Decorative Colors: Reflect the brand identity or give a branded feel to a composition. + + + Tip: Use elevation colors for hierarchy, semantic colors for purpose, and branded colors for identity and style. + + +## Shadow tokens + +Shadows are the most extreme way to display elevation. Overuse of shadows without understanding your page’s visual hierarchy can have a negative effect on the user experience. + +View our full list of Elevated shadow tokens +

+ +**Level 1 shadows** are accent shadows that subtly define the edges of a container. They serve as a lightweight alternative to risen shadows at lower elevations. + + + {ShadowTokensLevel1Example} + + +**Level 2 shadows** are small and sharp and indicate a close distance with the surface behind them. Shadows used in dark backgrounds use a slightly darker hue so the perceived depth is consistent. + + + {ShadowTokensLevel2Example} + + +**Level 3 shadows** are larger and softer, this indicates a further distance from the surface behind them. Shadows used in dark backgrounds use a slightly darker hue so the perceived depth is consistent. + + + {ShadowTokensLevel3Example} + +
+ +
diff --git a/packages/paste-website/stories/Elevation.stories.tsx b/packages/paste-website/stories/Elevation.stories.tsx new file mode 100644 index 0000000000..f474f6e9b3 --- /dev/null +++ b/packages/paste-website/stories/Elevation.stories.tsx @@ -0,0 +1,194 @@ +import { Box } from "@twilio-paste/box"; +import { DarkModeIcon } from "@twilio-paste/icons/esm/DarkModeIcon"; +import { ThemeIcon } from "@twilio-paste/icons/esm/ThemeIcon"; +import type { BackgroundColor, BoxShadow } from "@twilio-paste/style-props"; +import { Text } from "@twilio-paste/text"; +import { Theme } from "@twilio-paste/theme"; +import * as React from "react"; + +export const ElevatedBackgroundColorTokens = (): React.ReactNode => { + interface TokensTemplateProps { + backgroundColor: BackgroundColor; + backgroundColorElevated: BackgroundColor; + hasBorder?: boolean; + backgroundColorBase: string; + backgroundColorElevation: string; + } + + const TokensTemplate: React.FC = ({ + backgroundColor, + backgroundColorElevated, + hasBorder, + backgroundColorBase, + backgroundColorElevation, + }) => { + return ( + + + + + + + + Base: + + + {backgroundColorBase} + + + Elevation: + + + {backgroundColorElevation} + + + + ); + }; + return ( + + + + + + + ); +}; + +const ShadowTokens: React.FC<{ boxShadow: BoxShadow; boxShadowlabel: string }> = ({ boxShadow, boxShadowlabel }) => { + return ( + + + + + + + Selected theme + + + + {boxShadowlabel} + + + + + + + + + + Dark mode + + + + {boxShadowlabel} + + + + + + ); +}; + +export const ShadowTokensLevel1 = (): React.ReactNode => { + return ; +}; + +ShadowTokensLevel1.parameters = { + padding: false, +}; + +export const ShadowTokensLevel2 = (): React.ReactNode => { + return ; +}; + +ShadowTokensLevel2.parameters = { + padding: false, +}; + +export const ShadowTokensLevel3 = (): React.ReactNode => { + return ; +}; + +ShadowTokensLevel3.parameters = { + padding: false, +}; + +export default { + title: "Website/ElevationExamples", +};