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/elevation.mdx b/static/app/components/core/layout/elevation.mdx
new file mode 100644
index 00000000000000..6d51d61ca83564
--- /dev/null
+++ b/static/app/components/core/layout/elevation.mdx
@@ -0,0 +1,127 @@
+---
+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/elevation';
+import {Text} from 'sentry/components/core/text';
+import * as Storybook from 'sentry/stories';
+
+import APIReference from '!!type-loader!sentry/components/core/layout/elevation';
+
+export const types = {Slab: APIReference.Slab};
+
+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
+
+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.
+
+
+
+
+ Well
+
+
+ Slab
+
+
+ Floating Sheet
+
+
+
+```jsx
+Well
+Slab
+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
+```
+
+### 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/elevation.tsx b/static/app/components/core/layout/elevation.tsx
new file mode 100644
index 00000000000000..09cd98e9e1c5cd
--- /dev/null
+++ b/static/app/components/core/layout/elevation.tsx
@@ -0,0 +1,230 @@
+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);
+ }
+ }
+
+ &:has(> *[aria-disabled='true']),
+ &[aria-disabled='true'] {
+ > * {
+ 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/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 3e167eb0acb929..c9d5c61aadb8db 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/elevation';
+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}
+
);
}
@@ -64,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;
- }
-`;