- name
- design-system
- description
- Guide for using Sentry's layout and text primitives. Use when implementing UI components, layouts, or typography. Enforces use of core components over styled components.
# Layout and Text Primitives at Sentry
## Core Principle
**ALWAYS use core components from `@sentry/scraps` instead of creating styled components with Emotion.**
Core components provide consistent styling, responsive design, and better maintainability across the codebase.
## Component Implementation Reference
For the complete list of supported props and their types, refer to the implementation files:
- **Layout Components**: `/static/app/components/core/layout/`
- `container.tsx` - Base container with all layout props
- `flex.tsx` - Flex layout primitive
- `grid.tsx` - Grid layout primitive
- `stack.tsx` - Stack layout primitive (Flex with column direction by default)
- **Typography Components**: `/static/app/components/core/text/`
- `text.tsx` - Text primitive
- `heading.tsx` - Heading primitive
## Layout Primitives
> **Important**: `Flex`, `Grid`, and `Stack` all extend `Container`. This means **every prop available on Container is also available on Flex, Grid, and Stack**. When you use `<Flex>`, you get all Container props (position, padding, border, overflow, etc.) PLUS the flex-specific props. The same applies to Grid and Stack.
### Container
Base layout component that supports all common layout properties. Flex, Grid, and Stack extend Container, inheriting all of its props.
**Key Props** (see `container.tsx` for complete list):
- `position`: "static" | "relative" | "absolute" | "fixed" | "sticky"
- `padding`, `paddingTop`, `paddingBottom`, `paddingLeft`, `paddingRight`: SpaceSize tokens
- `margin`, `marginTop`, etc.: SpaceSize tokens (deprecated, prefer gap)
- `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight`
- `border`, `borderTop`, `borderBottom`, `borderLeft`, `borderRight`: BorderVariant tokens
- `radius`: RadiusSize tokens
- `overflow`, `overflowX`, `overflowY`: "visible" | "hidden" | "scroll" | "auto"
- `background`: SurfaceVariant ("primary" | "secondary" | "tertiary")
- `display`: Various display types
- Flex item props: `flex`, `flexGrow`, `flexShrink`, `flexBasis`, `alignSelf`, `order`
- Grid item props: `area`, `row`, `column`
```tsx
import {Container} from '@sentry/scraps/layout';
// ❌ Don't create styled components
const Component = styled('div')`
padding: ${p => p.theme.space.md};
border: 1px solid ${p => p.theme.tokens.border.primary};
`;
// ✅ Use Container primitive
<Container padding="md" border="primary">
Content
</Container>;
```
### Flex
Use `<Flex>` for flex layouts. Extends `Container`, inheriting all Container props plus flex-specific props.
**Flex-Specific Props** (see `flex.tsx` for complete list):
- `direction`: "row" | "row-reverse" | "column" | "column-reverse"
- `align`: "start" | "end" | "center" | "baseline" | "stretch"
- `justify`: "start" | "end" | "center" | "between" | "around" | "evenly" | "left" | "right"
- `gap`: SpaceSize or `"${SpaceSize} ${SpaceSize}"` for row/column gap
- `wrap`: "nowrap" | "wrap" | "wrap-reverse"
- `display`: "flex" | "inline-flex" | "none"
**Plus ALL Container props**: `position`, `padding`, `margin`, `width`, `height`, `border`, `radius`, `overflow`, `background`, flex/grid item props, and more (see Container section above).
```tsx
import {Flex} from '@sentry/scraps/layout';
// ❌ Don't create styled components
const Component = styled('div')`
display: flex;
flex-direction: column;
position: relative;
`;
// ✅ Use Flex primitive with props
<Flex direction="column" position="relative" gap="md">
<Child1 />
<Child2 />
</Flex>;
```
### Grid
Use `<Grid>` for grid layouts. Extends `Container`, inheriting all Container props plus grid-specific props.
**Grid-Specific Props** (see `grid.tsx` for complete list):
- `columns`: Grid template columns (number or CSS value)
- `rows`: Grid template rows
- `areas`: Named grid areas
- `gap`: SpaceSize or `"${SpaceSize} ${SpaceSize}"` for row/column gap
- `align`: "start" | "end" | "center" | "baseline" | "stretch" (align-items)
- `alignContent`: "start" | "end" | "center" | "between" | "around" | "evenly" | "stretch"
- `justify`: "start" | "end" | "center" | "between" | "around" | "evenly" | "stretch" (justify-content)
- `justifyItems`: "start" | "end" | "center" | "stretch"
- `flow`: "row" | "column" | "row dense" | "column dense"
- `autoColumns`, `autoRows`: Size of auto-generated tracks
**Plus ALL Container props**: `position`, `padding`, `margin`, `width`, `height`, `border`, `radius`, `overflow`, `background`, flex/grid item props, and more (see Container section above).
```tsx
import {Grid} from '@sentry/scraps/layout';
// ❌ Don't create styled components
const Component = styled('div')`
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: ${p => p.theme.space.md};
`;
// ✅ Use Grid primitive
<Grid columns="repeat(3, 1fr)" gap="md">
<Item1 />
<Item2 />
<Item3 />
</Grid>;
```
### Stack
Use `<Stack>` for vertical layouts. Stack is essentially `Flex` with `direction="column"` by default. It also provides `Stack.Separator` for adding separators between items.
**Props** (see `stack.tsx` for complete list):
- Same as Flex props (inherits all Flex and Container props)
- `direction` defaults to "column" (but can be overridden)
- `Stack.Separator` component for adding dividers between stack items
```tsx
import {Stack} from '@sentry/scraps/layout';
// ❌ Don't create styled components for vertical layouts
const Component = styled('div')`
display: flex;
flex-direction: column;
gap: ${p => p.theme.space.md};
`;
// ✅ Use Stack primitive (automatically column direction)
<Stack gap="md">
<Item1 />
<Item2 />
<Item3 />
</Stack>;
// ✅ With separators between items
<Stack gap="md">
<Item1 />
<Stack.Separator />
<Item2 />
<Stack.Separator />
<Item3 />
</Stack>;
// ✅ Stack supports all Flex and Container props
<Stack gap="md" padding="lg" position="relative" border="primary">
<Item1 />
<Item2 />
</Stack>;
```
## Typography Primitives
### Text
Use `<Text>` for all text content. Never use raw `<p>`, `<span>`, or `<div>` elements with text styling.
**Key Props** (see `text.tsx` for complete list):
- `as`: "span" | "p" | "label" | "div" (semantic HTML element)
- `size`: TextSize ("xs" | "sm" | "md" | "lg" | "xl" | "2xl")
- `variant`: ContentVariant | "muted" (see Content Variant Tokens below)
- `align`: "left" | "center" | "right" | "justify"
- `bold`: boolean
- `italic`: boolean
- `uppercase`: boolean
- `monospace`: boolean
- `tabular`: boolean (fixed-width numbers)
- `ellipsis`: boolean (truncate with ellipsis)
- `wrap`: "nowrap" | "normal" | "pre" | "pre-line" | "pre-wrap"
- `textWrap`: "wrap" | "nowrap" | "balance" | "pretty" | "stable"
- `wordBreak`: "normal" | "break-all" | "keep-all" | "break-word"
- `density`: "compressed" | "comfortable" (line-height)
- `underline`: boolean | "dotted"
- `strikethrough`: boolean
```tsx
import {Text} from '@sentry/scraps/text';
// ❌ Don't create styled text components
const Label = styled('span')`
color: ${p => p.theme.tokens.content.secondary};
font-size: ${p => p.theme.font.size.sm};
`;
// ❌ Don't use raw elements
<p>This is a paragraph</p>
<span>Status: Active</span>
// ✅ Use Text primitive with semantic 'as' prop
<Text as="p" variant="muted" density="comfortable">
This is a paragraph
</Text>
<Text as="span" bold uppercase>
Status: Active
</Text>
```
### Heading
Use `<Heading>` for all headings. Never use raw `<h1>`, `<h2>`, etc. elements.
**Key Props** (see `heading.tsx` for complete list):
- `as`: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" (REQUIRED)
- `size`: HeadingSize ("xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl")
- `variant`: Same as Text
- `align`: Same as Text
- `italic`, `monospace`, `tabular`: Same as Text
- `ellipsis`, `wrap`, `textWrap`, `wordBreak`: Same as Text
- `density`: Same as Text
- `underline`, `strikethrough`: Same as Text
Note: `bold` and `uppercase` are NOT available on Heading (headings are always bold).
```tsx
import {Heading} from '@sentry/scraps/text';
// ❌ Don't style heading elements
const Title = styled('h2')`
font-size: ${p => p.theme.font.size.md};
font-weight: bold;
`;
// ❌ Don't use raw heading elements
<h2>My Title</h2>
// ✅ Use Heading primitive with semantic 'as' prop
<Heading as="h2">My Title</Heading>
// ✅ With custom size
<Heading as="h3" size="xl">Large H3</Heading>
```
## Info Components
> **Important**: Always prefer `InfoTip` and `InfoText` over using raw `<Tooltip>` components. These provide consistent, accessible patterns for contextual help.
### InfoTip
Use `<InfoTip>` to add an info icon with tooltip next to labels or headings. It's keyboard accessible and provides a consistent pattern for supplementary help.
```tsx
import {InfoTip} from '@sentry/scraps/info';
import {Flex} from '@sentry/scraps/layout';
import {Text} from '@sentry/scraps/text';
// ❌ Don't use Tooltip with arbitrary icons
<Flex gap="xs" align="center">
<Text>Retention Period</Text>
<Tooltip title="The number of days...">
<IconInfo size="xs" />
</Tooltip>
</Flex>
// ✅ Use InfoTip for contextual help icons
<Flex gap="xs" align="center">
<Text>Retention Period</Text>
<InfoTip title="The number of days event data is stored before being automatically deleted." />
</Flex>
```
**Key Props**:
- `title`: Tooltip content (required)
- `size`: "xs" | "sm" (default) | "md"
**When to Use**:
- Add context to headings or section titles
- Show supplementary information without inline text
- Explain settings or configuration options
### InfoText
Use `<InfoText>` for inline text with a tooltip. It renders text with a dotted underline that reveals a tooltip on hover/focus.
```tsx
import {InfoText} from '@sentry/scraps/info';
// ❌ Don't wrap text with raw Tooltip
<Tooltip title="Time to First Byte measures the time...">
<span style={{textDecoration: 'underline dotted'}}>TTFB</span>
View on GitHub