From 354801f0bd3e2299e2ef65d731fc022a1c94796f Mon Sep 17 00:00:00 2001 From: JonasBa Date: Mon, 6 Oct 2025 11:34:52 -0700 Subject: [PATCH 1/5] core: use slab to style link --- .../components/core/button/styles.chonk.tsx | 1 - static/app/components/core/layout/layer.mdx | 94 ++++++++ static/app/components/core/layout/layer.tsx | 208 ++++++++++++++++++ static/app/stories/view/storyFooter.tsx | 135 +++++++----- 4 files changed, 380 insertions(+), 58 deletions(-) create mode 100644 static/app/components/core/layout/layer.mdx create mode 100644 static/app/components/core/layout/layer.tsx diff --git a/static/app/components/core/button/styles.chonk.tsx b/static/app/components/core/button/styles.chonk.tsx index f4ab2764e63e59..ac51c9705584ee 100644 --- a/static/app/components/core/button/styles.chonk.tsx +++ b/static/app/components/core/button/styles.chonk.tsx @@ -70,7 +70,6 @@ export function DO_NOT_USE_getChonkButtonStyles( justifyContent: 'center', fontWeight: p.theme.fontWeight.bold, - opacity: p.busy || p.disabled ? 0.6 : undefined, cursor: 'pointer', diff --git a/static/app/components/core/layout/layer.mdx b/static/app/components/core/layout/layer.mdx new file mode 100644 index 00000000000000..2e156dbd34c287 --- /dev/null +++ b/static/app/components/core/layout/layer.mdx @@ -0,0 +1,94 @@ +--- +title: Layer +description: A specialized container component that provides pre-styled surface variants for layers +source: 'sentry/components/core/layout/layer' +resources: + js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/layer.tsx +--- + +import {Container, Flex} from 'sentry/components/core/layout'; +import {FloatingSheet, Slab, Well} from 'sentry/components/core/layout/layer'; +import {Text} from 'sentry/components/core/text'; +import * as Storybook from 'sentry/stories'; + +import APIReference from '!!type-loader!sentry/components/core/layout/layer'; + +export const types = {Slab: APIReference.Slab}; + +The `Slab` component creates interactive surfaces with an expressed thickness dimension through elevated bottom borders. Slabs are part of Sentry's [layering and elevation system](/stories/principles/layering-and-elevation), designed to make interactive elements stand out by giving them a three-dimensional appearance—like keys protruding from a keyboard. + +Unlike flat sheets, slabs use "chonky borders" that are thicker at the bottom, creating an isometric perspective effect. This visual design helps users identify interactive elements and establishes clear information hierarchy. + +## Slabs, Wells and Floating Sheets + +The Layer module provides three primary surface components: `Well`, `Slab` and `FloatingSheet`. + + + + + Default Well + + + Default Slab + + + Default Floating Sheet + + + +```jsx +Default Well +Default Slab +Default Floating Sheet +``` + +### Elevation + +Control the elevation and thickness of the slab or floating sheets using the `size` prop. Available sizes are `xs`, `sm`, and `md` (default). Smaller sizes create a more subtle elevated effect. + + + + + + Slab + + xs + + + sm + + + md + + + + Floating Sheet + + xs + + + sm + + + md + + + + + +```jsx +Extra small elevation +Small elevation +Default elevation + +Extra small elevation +Small elevation +Default elevation +``` diff --git a/static/app/components/core/layout/layer.tsx b/static/app/components/core/layout/layer.tsx new file mode 100644 index 00000000000000..b915fbd14187c8 --- /dev/null +++ b/static/app/components/core/layout/layer.tsx @@ -0,0 +1,208 @@ +import {type DO_NOT_USE_ChonkTheme} from '@emotion/react'; +import styled from '@emotion/styled'; + +import {chonkFor, debossedBackground} from 'sentry/components/core/chonk'; +import { + Container, + type ContainerElement, + type ContainerProps, +} from 'sentry/components/core/layout/container'; +import {rc, type Responsive} from 'sentry/components/core/layout/styles'; +import {isChonkTheme} from 'sentry/utils/theme/withChonk'; + +const slabElevation = { + md: '2px', + sm: '2px', + xs: '1px', +} as const; + +const slabHoverElevation = '1px'; +interface SlabLayoutProps { + size?: NonNullable>; + variant?: 'accent' | 'warning' | 'danger'; +} + +type SlabProps = ContainerProps & SlabLayoutProps; + +export function Slab(props: SlabProps) { + const {variant, border, radius = 'md', size = 'md', ...rest} = props; + + return ( + + + + ); +} + +function getSlabContainerTheme( + variant: SlabLayoutProps['variant'], + theme: DO_NOT_USE_ChonkTheme +) { + switch (variant) { + case 'accent': + return { + surface: theme.colors.blue400, + background: chonkFor(theme, theme.colors.blue400), + }; + case 'warning': + return { + surface: theme.colors.chonk.yellow400, + background: chonkFor(theme, theme.colors.chonk.yellow400), + }; + case 'danger': + return { + surface: theme.colors.chonk.red400, + background: chonkFor(theme, theme.colors.chonk.red400), + }; + default: + return { + surface: theme.colors.surface500, + background: theme.colors.surface100, + }; + } +} + +const SlabContainer = styled(Container)<{ + size: SlabLayoutProps['size']; + variant: SlabLayoutProps['variant']; +}>` + background-color: ${p => + isChonkTheme(p.theme) + ? getSlabContainerTheme(p.variant, p.theme).background + : p.theme.tokens.background.primary}; + + display: ${p => (isChonkTheme(p.theme) ? undefined : 'contents')}; + + > * { + ${p => + rc( + 'transform', + p.size, + p.theme, + (value, _breakpoint, _theme) => `translateY(-${slabElevation[value]})` + )}; + transition: transform ${p => p.theme.motion.snap.fast}; + } + + &:hover { + > * { + ${p => + rc( + 'transform', + p.size, + p.theme, + (value, _breakpoint, _theme) => + `translateY(calc(-${slabElevation[value]} - ${slabHoverElevation}))` + )}; + } + } + + &:active { + > * { + transform: translateY(0); + } + } +`; + +interface WellLayoutProps {} +type WellProps = ContainerProps & WellLayoutProps; + +export const Well = styled((props: WellProps) => { + const {radius = 'md', ...rest} = props; + + return ; +})` + ${p => { + if (isChonkTheme(p.theme)) { + return { + boxShadow: `0px 2px 0px 0px ${p.theme.tokens.border.primary} inset`, + ...debossedBackground(p.theme), + }; + } + return { + backgroundColor: p.theme.tokens.background.primary, + }; + }} +`; + +interface FloatingSheetLayoutProps { + size?: Responsive<'sm' | 'md' | 'xs'>; +} +type FloatingSheetProps = ContainerProps & + FloatingSheetLayoutProps; + +export const FloatingSheet = styled( + (props: FloatingSheetProps) => { + const { + size = 'md', + radius = 'md', + background = 'primary', + border = 'primary', + ...rest + } = props; + + return ( + + + + ); + } +)``; + +const floatingSheetElevation = { + md: '6px', + sm: '5px', + xs: '4px', +} as const; + +const FloatingSheetContainer = styled(Container)<{ + size: FloatingSheetLayoutProps['size']; +}>` + position: relative; + ${p => + rc( + 'transform', + p.size, + p.theme, + (value, _breakpoint, _theme) => `translateY(-${floatingSheetElevation[value]})` + )} + + > * { + position: relative; + z-index: 1; + } + + &:after { + content: ''; + position: absolute; + top: 0; + left: ${p => p.theme.borderRadius}; + right: ${p => p.theme.borderRadius}; + bottom: 0; + background-color: ${p => + isChonkTheme(p.theme) + ? p.theme.colors.surface100 + : p.theme.tokens.background.secondary}; + border-radius: ${p => + isChonkTheme(p.theme) ? p.theme.radius.md : p.theme.borderRadius}; + z-index: 0; + ${p => + rc( + 'transform', + p.size, + p.theme, + (value, _breakpoint, _theme) => `translateY(${floatingSheetElevation[value]})` + )}; + } +`; diff --git a/static/app/stories/view/storyFooter.tsx b/static/app/stories/view/storyFooter.tsx index 3e167eb0acb929..9d67ee74132f19 100644 --- a/static/app/stories/view/storyFooter.tsx +++ b/static/app/stories/view/storyFooter.tsx @@ -1,7 +1,6 @@ -import styled from '@emotion/styled'; - -import {LinkButton} from 'sentry/components/core/button/linkButton'; -import {Flex} from 'sentry/components/core/layout'; +import {Flex, Grid, Stack} from 'sentry/components/core/layout'; +import {Slab} from 'sentry/components/core/layout/layer'; +import {Link} from 'sentry/components/core/link'; import {Text} from 'sentry/components/core/text'; import {IconArrow} from 'sentry/icons'; @@ -16,32 +15,54 @@ export function StoryFooter() { const pagination = findPreviousAndNextStory(story, stories); return ( - - {pagination?.prev && ( - }> - - Previous - - - {pagination.prev.label} - - - )} - {pagination?.next && ( - } - > - - Next - - - {pagination.next.label} - - - )} - + + {pagination?.prev ? ( + + {props => { + return ( + + + + + + + + Previous + + + {pagination.prev?.label} + + + + + ); + }} + + ) : null} + {pagination?.next ? ( + + {props => { + return ( + + + + + Next + + + {pagination.next?.label} + + + + + + + + ); + }} + + ) : null} + ); } @@ -65,30 +86,30 @@ function findPreviousAndNextStory( }; } -const Card = styled(LinkButton)` - display: flex; - flex-direction: column; - flex: 1; - height: 80px; - margin-bottom: ${p => p.theme.space['3xl']}; - span:last-child { - width: 100%; - display: grid; - grid-template-areas: - 'icon label' - 'icon text'; - grid-template-columns: auto 1fr; - place-content: center; - gap: ${p => p.theme.space.md} ${p => p.theme.space.xl}; - } - &[data-flip] span:last-child { - grid-template-areas: - 'label icon' - 'text icon'; - grid-template-columns: 1fr auto; - justify-content: flex-end; - } - span:has(svg) { - grid-area: icon; - } -`; +// const Card = styled(LinkButton)` +// display: flex; +// flex-direction: column; +// flex: 1; +// height: 80px; +// margin-bottom: ${p => p.theme.space['3xl']}; +// span:last-child { +// width: 100%; +// display: grid; +// grid-template-areas: +// 'icon label' +// 'icon text'; +// grid-template-columns: auto 1fr; +// place-content: center; +// gap: ${p => p.theme.space.md} ${p => p.theme.space.xl}; +// } +// &[data-flip] span:last-child { +// grid-template-areas: +// 'label icon' +// 'text icon'; +// grid-template-columns: 1fr auto; +// justify-content: flex-end; +// } +// span:has(svg) { +// grid-area: icon; +// } +// `; From 6bb4300241b606d3e1d0399242c5a652402dc92d Mon Sep 17 00:00:00 2001 From: JonasBa Date: Mon, 6 Oct 2025 11:34:52 -0700 Subject: [PATCH 2/5] core: use slab to style link --- static/app/components/core/layout/layer.mdx | 55 +++++++++++++++---- static/app/components/core/layout/layer.tsx | 7 +++ static/app/components/core/link/link.tsx | 2 +- .../layering-and-elevation.mdx | 4 ++ static/app/stories/view/storyFooter.tsx | 28 ---------- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/static/app/components/core/layout/layer.mdx b/static/app/components/core/layout/layer.mdx index 2e156dbd34c287..99533de54c1b02 100644 --- a/static/app/components/core/layout/layer.mdx +++ b/static/app/components/core/layout/layer.mdx @@ -1,6 +1,6 @@ --- title: Layer -description: A specialized container component that provides pre-styled surface variants for layers +description: Specialized container components for creating surfaces with different elevation levels—Slabs, Wells, and Floating Sheets source: 'sentry/components/core/layout/layer' resources: js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/layer.tsx @@ -15,31 +15,35 @@ import APIReference from '!!type-loader!sentry/components/core/layout/layer'; export const types = {Slab: APIReference.Slab}; -The `Slab` component creates interactive surfaces with an expressed thickness dimension through elevated bottom borders. Slabs are part of Sentry's [layering and elevation system](/stories/principles/layering-and-elevation), designed to make interactive elements stand out by giving them a three-dimensional appearance—like keys protruding from a keyboard. - -Unlike flat sheets, slabs use "chonky borders" that are thicker at the bottom, creating an isometric perspective effect. This visual design helps users identify interactive elements and establishes clear information hierarchy. +The Layer module provides specialized container components that implement Sentry's [layering and elevation system](/stories/principles/layering-and-elevation). These components create visual hierarchy and help users identify interactive elements through three-dimensional depth effects. ## Slabs, Wells and Floating Sheets -The Layer module provides three primary surface components: `Well`, `Slab` and `FloatingSheet`. +Each component serves a specific purpose in the layering hierarchy: + +- **Well**: Creates debossed surfaces that appear recessed into the background. Wells have increased top borders (inset box-shadow) and use a darker background to indicate lower elevation. Use wells for grouping related content in a visually contained area. + +- **Slab**: Creates embossed surfaces with expressed thickness through elevated bottom borders ("chonky borders"). Slabs appear to protrude from the surface, making them ideal for interactive elements like buttons and clickable cards. The border thickness corresponds to the element's depth. + +- **FloatingSheet**: Creates elevated surfaces that hover above the main content layer. Unlike slabs and wells which are attached to the content surface, floating sheets cast shadows on the content beneath them to indicate higher elevation. They don't use chonky borders, making them suitable for overlays, modals, and tooltips. - Default Well + Well - Default Slab + Slab - Default Floating Sheet + Floating Sheet ```jsx -Default Well -Default Slab -Default Floating Sheet +Well +Slab +Floating Sheet ``` ### Elevation @@ -92,3 +96,32 @@ Control the elevation and thickness of the slab or floating sheets using the `si Small elevation Default elevation ``` + +### Slab Variants + +Slabs support color variants for different semantic purposes. The `variant` prop changes both the surface and background colors to indicate accent, warning, or danger states. + + + + + + Default + + + Accent + + + Warning + + + Danger + + + + +```jsx +Default +Accent +Warning +Danger +``` diff --git a/static/app/components/core/layout/layer.tsx b/static/app/components/core/layout/layer.tsx index b915fbd14187c8..a6d0f1539dad12 100644 --- a/static/app/components/core/layout/layer.tsx +++ b/static/app/components/core/layout/layer.tsx @@ -107,6 +107,13 @@ const SlabContainer = styled(Container)<{ transform: translateY(0); } } + + &:has(> *[aria-disabled='true']), + &[aria-disabled='true'] { + > * { + transform: translateY(0); + } + } `; interface WellLayoutProps {} diff --git a/static/app/components/core/link/link.tsx b/static/app/components/core/link/link.tsx index d4caa66f22dd52..4f0de8e88b4e24 100644 --- a/static/app/components/core/link/link.tsx +++ b/static/app/components/core/link/link.tsx @@ -77,7 +77,7 @@ export const Link = styled(({disabled, to, ...props}: LinkProps) => { const {state: appState} = useFrontendVersion(); if (disabled || !location) { - return ; + return ; } return ( diff --git a/static/app/components/core/principles/layering-and-elevation/layering-and-elevation.mdx b/static/app/components/core/principles/layering-and-elevation/layering-and-elevation.mdx index 9a87d28d259158..c3174d89081672 100644 --- a/static/app/components/core/principles/layering-and-elevation/layering-and-elevation.mdx +++ b/static/app/components/core/principles/layering-and-elevation/layering-and-elevation.mdx @@ -74,6 +74,10 @@ Apply elevation in a manner consistent with the model outlined below. **Slabs and wells** are sheets with an expressed thickness dimension, i.e. those with increased bottom borders (embossed slabs) or top borders (debossed wells). The border thickness corresponds to the element's thickness/depth. Larger buttons, for example, are thicker and therefore have more Chonky borders. Use slabs and wells for interactive elements to help make them stand out. +See the [Slab](/stories/layout/layer#slabs-wells-and-floating-sheets) and [Well](/stories/layout/layer#slabs-wells-and-floating-sheets) components. + ### Floating Elements **Floating elements** are sheets that hover above the main content (as opposed to slabs and wells, which are still attached to the content) and therefore cast a shadow on the content underneath. This group includes tooltips, overlays, modals, and toasts. They do not need to have a thickness dimension — no Chonky border. + +See the [FloatingSheet](/stories/layout/layer#slabs-wells-and-floating-sheets) component. diff --git a/static/app/stories/view/storyFooter.tsx b/static/app/stories/view/storyFooter.tsx index 9d67ee74132f19..b165f685683095 100644 --- a/static/app/stories/view/storyFooter.tsx +++ b/static/app/stories/view/storyFooter.tsx @@ -85,31 +85,3 @@ function findPreviousAndNextStory( next: stories[currentIndex + 1] ?? undefined, }; } - -// const Card = styled(LinkButton)` -// display: flex; -// flex-direction: column; -// flex: 1; -// height: 80px; -// margin-bottom: ${p => p.theme.space['3xl']}; -// span:last-child { -// width: 100%; -// display: grid; -// grid-template-areas: -// 'icon label' -// 'icon text'; -// grid-template-columns: auto 1fr; -// place-content: center; -// gap: ${p => p.theme.space.md} ${p => p.theme.space.xl}; -// } -// &[data-flip] span:last-child { -// grid-template-areas: -// 'label icon' -// 'text icon'; -// grid-template-columns: 1fr auto; -// justify-content: flex-end; -// } -// span:has(svg) { -// grid-area: icon; -// } -// `; From 8b88e04989444fc1f44877c6a497a65f14bd3f61 Mon Sep 17 00:00:00 2001 From: JonasBa Date: Tue, 7 Oct 2025 06:31:29 -0700 Subject: [PATCH 3/5] rename to elevation --- .../core/layout/{layer.mdx => elevation.mdx} | 10 +++++----- .../core/layout/{layer.tsx => elevation.tsx} | 0 static/app/stories/view/storyFooter.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename static/app/components/core/layout/{layer.mdx => elevation.mdx} (91%) rename static/app/components/core/layout/{layer.tsx => elevation.tsx} (100%) diff --git a/static/app/components/core/layout/layer.mdx b/static/app/components/core/layout/elevation.mdx similarity index 91% rename from static/app/components/core/layout/layer.mdx rename to static/app/components/core/layout/elevation.mdx index 99533de54c1b02..a95720c60fa98f 100644 --- a/static/app/components/core/layout/layer.mdx +++ b/static/app/components/core/layout/elevation.mdx @@ -1,21 +1,21 @@ --- -title: Layer +title: Elevation components description: Specialized container components for creating surfaces with different elevation levels—Slabs, Wells, and Floating Sheets source: 'sentry/components/core/layout/layer' resources: js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/layer.tsx --- -import {Container, Flex} from 'sentry/components/core/layout'; -import {FloatingSheet, Slab, Well} from 'sentry/components/core/layout/layer'; +import {Container, Flex} from 'sentry/components/core/elevation'; +import {FloatingSheet, Slab, Well} from 'sentry/components/core/layout/elevation'; import {Text} from 'sentry/components/core/text'; import * as Storybook from 'sentry/stories'; -import APIReference from '!!type-loader!sentry/components/core/layout/layer'; +import APIReference from '!!type-loader!sentry/components/core/layout/elevation'; export const types = {Slab: APIReference.Slab}; -The Layer module provides specialized container components that implement Sentry's [layering and elevation system](/stories/principles/layering-and-elevation). These components create visual hierarchy and help users identify interactive elements through three-dimensional depth effects. +The Elevation module provides specialized container components that implement Sentry's [layering and elevation system](/stories/principles/layering-and-elevation). These components create visual hierarchy and help users identify interactive elements through three-dimensional depth effects. ## Slabs, Wells and Floating Sheets diff --git a/static/app/components/core/layout/layer.tsx b/static/app/components/core/layout/elevation.tsx similarity index 100% rename from static/app/components/core/layout/layer.tsx rename to static/app/components/core/layout/elevation.tsx diff --git a/static/app/stories/view/storyFooter.tsx b/static/app/stories/view/storyFooter.tsx index b165f685683095..c9d5c61aadb8db 100644 --- a/static/app/stories/view/storyFooter.tsx +++ b/static/app/stories/view/storyFooter.tsx @@ -1,5 +1,5 @@ import {Flex, Grid, Stack} from 'sentry/components/core/layout'; -import {Slab} from 'sentry/components/core/layout/layer'; +import {Slab} from 'sentry/components/core/layout/elevation'; import {Link} from 'sentry/components/core/link'; import {Text} from 'sentry/components/core/text'; import {IconArrow} from 'sentry/icons'; From 8a4ddefd02e2a07b14904a89826e514b8347b53e Mon Sep 17 00:00:00 2001 From: JonasBa Date: Tue, 7 Oct 2025 06:37:07 -0700 Subject: [PATCH 4/5] rename import --- static/app/components/core/layout/elevation.mdx | 9 +++++++-- static/app/components/core/layout/elevation.tsx | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/static/app/components/core/layout/elevation.mdx b/static/app/components/core/layout/elevation.mdx index a95720c60fa98f..1bd0095f5364f1 100644 --- a/static/app/components/core/layout/elevation.mdx +++ b/static/app/components/core/layout/elevation.mdx @@ -6,8 +6,13 @@ resources: js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/layer.tsx --- -import {Container, Flex} from 'sentry/components/core/elevation'; -import {FloatingSheet, Slab, Well} from 'sentry/components/core/layout/elevation'; +import { + Container, + Flex, + FloatingSheet, + Slab, + Well, +} from 'sentry/components/core/layout/elevation'; import {Text} from 'sentry/components/core/text'; import * as Storybook from 'sentry/stories'; diff --git a/static/app/components/core/layout/elevation.tsx b/static/app/components/core/layout/elevation.tsx index a6d0f1539dad12..09cd98e9e1c5cd 100644 --- a/static/app/components/core/layout/elevation.tsx +++ b/static/app/components/core/layout/elevation.tsx @@ -28,7 +28,22 @@ export function Slab(props: SlabProps) { const {variant, border, radius = 'md', size = 'md', ...rest} = props; return ( - + Date: Tue, 7 Oct 2025 06:43:01 -0700 Subject: [PATCH 5/5] fix import --- static/app/components/core/layout/elevation.mdx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/static/app/components/core/layout/elevation.mdx b/static/app/components/core/layout/elevation.mdx index 1bd0095f5364f1..6d51d61ca83564 100644 --- a/static/app/components/core/layout/elevation.mdx +++ b/static/app/components/core/layout/elevation.mdx @@ -1,18 +1,13 @@ --- -title: Elevation components +title: Elevation Components description: Specialized container components for creating surfaces with different elevation levels—Slabs, Wells, and Floating Sheets source: 'sentry/components/core/layout/layer' resources: js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/layer.tsx --- -import { - Container, - Flex, - FloatingSheet, - Slab, - Well, -} from 'sentry/components/core/layout/elevation'; +import {Container, Flex} from 'sentry/components/core/layout'; +import {FloatingSheet, Slab, Well} from 'sentry/components/core/layout/elevation'; import {Text} from 'sentry/components/core/text'; import * as Storybook from 'sentry/stories';