ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Sentry Design System 前端开发指南:布局与排版原语(Layout Text Primitives)最佳实践

Sentry Design System 前端开发指南:布局与排版原语(Layout  Text Primitives)最佳实践 Sentry Design System 前端开发指南布局与排版原语Layout Text Primitives最佳实践【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry本指南整理自仓库内.agents/skills/design-system/SKILL.md面向在 Sentry 前端static/app中编写 UI 组件、布局与排版的开发者。它系统讲解以sentry/scraps暴露的Container、Flex、Grid、Stack、Text、Heading、InfoTip、InfoText等核心组件替代 Emotion styled component 的完整用法、Props 类型、Token 取值与响应式断点语法。读完本文你将掌握 Sentry 现代 UI 的书写规范能在实际开发中写出样式统一、支持容器查询响应式、易于长期维护的界面代码。核心原则优先使用sentry/scraps核心组件而不是新建 styled component该技能文档全文贯穿一个强制约定ALWAYS use core components fromsentry/scrapsinstead of creating styled components with Emotion.即任何时候都应从sentry/scraps引入核心组件而非用 Emotion 的styled()手工创建带样式的组件。核心组件在代码库范围内提供了统一的样式、内置的响应式设计能力以及更好的可维护性。需要强调的是sentry/scraps并非虚构包名在static/app下有数百个文件直接import ... from sentry/scraps/...例如static/app/scrapsProviders/index.tsx引入sentry/scraps/translationContext而ScrapsProviders通过组合SentryFormErrorProvider、TranslationContextProvider、SentryDateTimeProvider、SentryLinkBehaviorProvider为整棵组件树注入国际化、日期与链接行为。所有核心组件的实际实现集中在 static/app/components/core/ 目录SKILL 文档中给出的请查阅实现文件路径均可一一对应布局组件static/app/components/core/layout/container.tsx — 承载全部布局 Props 的基础容器flex.tsx — Flex 布局原语grid.tsx — Grid 布局原语stack.tsx — 纵向布局原语默认directioncolumnsurface.tsx — 表面背景容器排版组件static/app/components/core/text/text.tsx — 文本原语heading.tsx — 标题原语从 layout/index.tsx 可以看到对外统一导出的公共 APIContainer、Flex、Grid、Stack、Surface以及响应式相关的useContainerBreakpoint、useHasContainerQuery、useResponsivePropValue、rc等工具。这意味着即使底层模块路径发生变化业务代码只要面向sentry/scraps/layout即可保持稳定。布局原语Container / Flex / Grid / Stack继承关系Flex、Grid、Stack 全部继承 Container这是布局体系最重要的一条信息Flex、Grid和Stack都继承自Container因此Container 上的每一个 Props 在 Flex、Grid、Stack 上都可用。例如使用Flex时你不仅获得 flex 专属能力还能直接使用position、padding、border、overflow、background等全部 Container PropsGrid、Stack 同理。这样设计的目的在于一个组件通过组合 Props 即可覆盖绝大多数布局需求避免为了一个定位需求再造一个 styled component。Container基础布局组件支持所有通用布局属性。Container 的关键 Props完整列表以 container.tsx 中的 TS 类型为准Props类型取值说明positionstatic \| relative \| absolute \| fixed \| sticky定位方式padding/paddingTop/paddingBottom/paddingLeft/paddingRightSpaceSize Token内边距margin/marginTop等SpaceSize Token外边距已废弃优先用父容器的gapwidth、height、minWidth、maxWidth、minHeight、maxHeightCSS 值盒模型尺寸border/borderTop/borderBottom/borderLeft/borderRightBorderVariant Token边框radiusRadiusSize Token圆角overflow/overflowX/overflowYvisible \| hidden \| scroll \| auto溢出控制backgroundSurfaceVariantprimary \| secondary \| tertiary背景display各类 display 值显示方式Flex 子项flex、flexGrow、flexShrink、flexBasis、alignSelf、order作为 flex item 时使用Grid 子项area、row、column作为 grid item 时使用从源码看这些 Props 均声明为Responsive...类型且大量支持四向简写ShorthandSpaceSize, 4例如paddingmd lg。注意 container.tsx 中对margin系列属性的 JSDoc 明确标注deprecated Use the gap prop on Flex or Grid instead与 SKILL 文档Prefer Gap/Padding Over Margin的倡导完全一致。反例 vs 正例import {Container} from sentry/scraps/layout; // ❌ 不要创建 styled component const Component styled(div) padding: ${p p.theme.space.md}; border: 1px solid ${p p.theme.tokens.border.primary}; ; // ✅ 使用 Container 原语 Container paddingmd borderprimary Content /Container;Flex使用Flex处理 flex 布局。它继承 Container 的全部 Props并叠加以下 flex 专属 PropsProps类型取值directionrow \| row-reverse \| column \| column-reversealignalign-itemsstart \| end \| center \| baseline \| stretchjustifyjustify-contentstart \| end \| center \| between \| around \| evenly \| left \| rightgapSpaceSize或${SpaceSize} ${SpaceSize}分别表示 row-gap 与 column-gapwrapnowrap \| wrap \| wrap-reversedisplayflex \| inline-flex \| noneimport {Flex} from sentry/scraps/layout; // ❌ 不要创建 styled component const Component styled(div) display: flex; flex-direction: column; position: relative; ; // ✅ 用 Flex 原语 Props Flex directioncolumn positionrelative gapmd Child1 / Child2 / /Flex;Grid使用Grid处理 grid 布局继承 Container 全部 Props 并叠加Props类型取值columns/rowsgrid 模板列 / 行数字或 CSS 值areas命名的 grid 区域gapSpaceSize 或${SpaceSize} ${SpaceSize}alignalign-itemsstart \| end \| center \| baseline \| stretchalignContentstart \| end \| center \| between \| around \| evenly \| stretchjustifyjustify-contentstart \| end \| center \| between \| around \| evenly \| stretchjustifyItemsstart \| end \| center \| stretchflowrow \| column \| row dense \| column denseautoColumns/autoRows自动生成轨道的大小import {Grid} from sentry/scraps/layout; // ❌ 不要创建 styled component const Component styled(div) display: grid; grid-template-columns: repeat(3, 1fr); gap: ${p p.theme.space.md}; ; // ✅ 用 Grid 原语 Grid columnsrepeat(3, 1fr) gapmd Item1 / Item2 / Item3 / /Grid;StackStack面向纵向布局场景本质上是directioncolumn的 Flex同时额外提供Stack.Separator用于在子项之间插入分隔线。关键点Props 与 Flex 完全一致继承 Flex 与 Container 的全部 Propsdirection默认为column可覆盖自带Stack.Separator子组件免去手工画分隔线。import {Stack} from sentry/scraps/layout; // ❌ 不要为纵向布局创建 styled component const Component styled(div) display: flex; flex-direction: column; gap: ${p p.theme.space.md}; ; // ✅ 使用 Stack 原语自动 column 方向 Stack gapmd Item1 / Item2 / Item3 / /Stack; // ✅ 子项之间插入分隔线 Stack gapmd Item1 / Stack.Separator / Item2 / Stack.Separator / Item3 / /Stack; // ✅ Stack 支持所有 Flex/Container Props Stack gapmd paddinglg positionrelative borderprimary Item1 / Item2 / /Stack;排版原语Text 与 HeadingTextText用于所有文本内容。规范要求绝不要使用带文本样式的裸p、span、div。关键 Props完整列表见 text.tsxProps类型取值说明asspan \| p \| label \| div渲染出的语义 HTML 元素sizeTextSizexs \| sm \| md \| lg \| xl \| 2xl字号variantContentVariant 或muted内容色alignleft \| center \| right \| justify对齐bold/italic/uppercase/monospaceboolean字重 / 斜体 / 大写 / 等宽tabularboolean固定宽度数字ellipsisboolean溢出省略号截断wrapnowrap \| normal \| pre \| pre-line \| pre-wrap换行textWrapwrap \| nowrap \| balance \| pretty \| stableCSS text-wrapwordBreaknormal \| break-all \| keep-all \| break-word断词规则densitycompressed \| comfortable行高密度underlineboolean |dotted下划线可点状strikethroughboolean删除线import {Text} from sentry/scraps/text; // ❌ 不要创建 styled 文本组件 const Label styled(span) color: ${p p.theme.tokens.content.secondary}; font-size: ${p p.theme.font.size.sm}; ; // ❌ 不要使用裸元素 pThis is a paragraph/p spanStatus: Active/span // ✅ 用 Text 原语 语义化 as Text asp variantmuted densitycomfortable This is a paragraph /Text Text asspan bold uppercase Status: Active /TextHeadingHeading用于所有标题绝不要使用裸h1–h6元素。其asProps 是必填项。关键 Props见 heading.tsxas:h1 \| h2 \| h3 \| h4 \| h5 \| h6必填size: HeadingSizexs \| sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xlvariant/align/italic/monospace/tabular与 Text 相同ellipsis/wrap/textWrap/wordBreak/density与 Text 相同underline/strikethrough与 Text 相同注意Heading不支持bold与uppercase标题本身就应当是加粗的语义上不应再叠加。import {Heading} from sentry/scraps/text; // ❌ 不要自己 style 标题元素 const Title styled(h2) font-size: ${p p.theme.font.size.md}; font-weight: bold; ; // ❌ 不要使用裸标题元素 h2My Title/h2 // ✅ 用 Heading 原语 语义化 as Heading ash2My Title/Heading // ✅ 自定义字号 Heading ash3 sizexlLarge H3/Heading上下文帮助组件InfoTip 与 InfoText规范要求优先使用InfoTip和InfoText而不是直接使用裸Tooltip。它们为上下文帮助提供一致、可访问的模式。InfoTip用于在标签或标题旁放置一个信息图标 tooltip。它支持键盘访问为补充说明提供统一模式。实现位于 static/app/components/core/info/infoTip.tsx。关键 Propstitletooltip 内容必填sizexs \| sm默认\| mdimport {InfoTip} from sentry/scraps/info; import {Flex} from sentry/scraps/layout; import {Text} from sentry/scraps/text; // ❌ 不要用 Tooltip 任意图标 Flex gapxs aligncenter TextRetention Period/Text Tooltip titleThe number of days... IconInfo sizexs / /Tooltip /Flex // ✅ 使用 InfoTip Flex gapxs aligncenter TextRetention Period/Text InfoTip titleThe number of days event data is stored before being automatically deleted. / /Flex适用场景为标题或区块标题补充语境在不增加正文文字的情况下展示补充信息解释设置项或配置项的含义。InfoText用于带 tooltip 的行内文本。它把文本渲染为点状下划线悬停 / 聚焦时展示 tooltip。实现位于 static/app/components/core/info/infoText.tsx。关键 Propstitletooltip 内容必填继承Text因此支持size、variant、bold等全部 Text Propsimport {InfoText} from sentry/scraps/info; // ❌ 不要用裸 Tooltip 包文本 Tooltip titleTime to First Byte measures the time... span style{{textDecoration: underline dotted}}TTFB/span /Tooltip // ✅ 用 InfoText 做行内解释 InfoText titleTime to First Byte measures the time from the request start until the first byte of the response is received. TTFB /InfoText // 组合 Text 样式 Props InfoText titleSmall muted text sizesm variantmuted Hint text /InfoText InfoText titleBold text bold Important term /InfoText适用场景行内定义技术术语或缩写提供附加语境但不增加视觉噪音形成一致、可访问的行内帮助模式。响应式设计容器查询优先于媒体查询响应式 Props 语法布局与排版原语的大多数 Props 都支持响应式语法——传入一个以断点 key 为键的对象// ❌ 不要写 styled media query const Component styled(div) display: flex; flex-direction: column; media screen and (min-width: ${p p.theme.breakpoints.md}) { flex-direction: row; } ; // ✅ 使用响应式 props 签名 Flex direction{{screen:xs: column, screen:md: row}}响应式 key 有两种形态且可以在同一个 Props 上混用裸 key如{xs: …}相对于最近的 query container解析——容器查询是默认行为因此无需前缀screen:前缀 key如{screen:md: …}相对于**视口viewport**解析。声明查询容器要响应组件自身可用的空间而非视口需要把父元素声明为查询容器containerTypeinline-size并在子元素上使用裸 key。一个元素永远无法查询自身尺寸所以声明容器的必须是它的祖先。从 container.tsx 的实现注释看containerType直接映射 CSScontainer-type优先使用inline-size只包含 inline宽度轴高度仍随内容流动size额外包含 block 轴此时元素高度必须由外部决定否则内容会塌缩——只有确实需要高度查询时才使用normal默认该元素不是尺寸查询容器子孙的裸 key 会向上解析到最近一个容器。// ✅ 根据父元素宽度重排而不是视口宽度 Container containerTypeinline-size Flex direction{{2xs: column, md: row}}{/* ... */}/Flex /ContainerJS 侧获取断点useContainerBreakpoint当需要在 JS 中拿到解析后的断点时使用useContainerBreakpoint(ref)。它是基于ResizeObserver、以容器为作用域的替代方案用来取代原来基于宽度的useMedia可以真正感知容器尺寸变化而非视口宽度。该 Hook 由 layout/index.tsx 对外导出。创建薄抽象Thin Abstractions的规则⚠️关键约束当意图只是 DRYDont Repeat Yourself地合并重复 Props 时在布局原语Container、Flex、Grid、Stack、Text、Heading之上创建抽象之前必须先征求用户确认。你可以在原语之上做很薄的抽象目的是用有意义的命名提升语义结构例如TableCell比泛化的Flex更语义化或为组件提供默认 Props。但要非常克制只在该抽象对可读性是净收益时才做。例如重复的 Props 只有两处且紧挨在一起那么抽象带来的间接成本就大于省下的篇幅。创建抽象之前必须完成四步询问用户是否确认说明打算创建的抽象论证该抽象为何值得增加复杂度获得明确批准后再继续。import {Flex, type FlexProps} from sentry/scraps/layout; // ❌ 不要到处重复相同 props Flex aligncenter gapxs flex1 paddingsmContent 1/Flex Flex aligncenter gapxs flex1 paddingsmContent 2/Flex Flex aligncenter gapxs flex1 paddingsmContent 3/Flex Flex aligncenter gapxs flex1 paddingsmContent 4/Flex // ✅ 创建带默认 props 的薄包装必须经用户确认后 function TableCell(props: FlexProps) { return Flex aligncenter gapmd {...props} /; } TableCellContent 1/TableCell TableCellContent 2/TableCell TableCell alignstartContent 3/TableCell{/* 可覆盖默认值 */}要点总结创建抽象前必须先征求用户确认继承原语的 Props 类型extends FlexProps在 JSX 组件上设置默认值并展开{...props}以允许覆盖不要使用 styled component——用原语组合来替代。通用编写准则1. 优先使用响应式 Props绝大多数 Props 支持用断点 key 写响应式值见上一节应以此替代手工编写的media查询。注意区分裸 key 面向查询容器与screen:前缀面向视口二者可在同一 Props 上混用。2. 优先使用 Gap / Padding而不是 MarginContainer 虽支持marginProps但已被废弃。请改用父容器上的gap// ❌ 不要用 margin 撑开子元素间距 const Child styled(div) margin-right: ${p p.theme.space.lg}; ; // ✅ 用父容器的 gap Flex gaplg Child1 / Child2 / /Flex;3. 布局与排版解耦不要把布局与排版耦合在同一个 styled component 里应使用各自的原语分开表达// ❌ 布局与排版耦合在一起 const Component styled(div) display: flex; flex-direction: column; color: ${p p.theme.tokens.content.secondary}; font-size: ${p p.theme.font.size.lg}; ; // ✅ 拆分为布局原语 排版原语 Flex directioncolumn Text variantmuted sizelg Content /Text /Flex;4. 需要完整 Props 时查阅实现文件实现文件里的 TypeScript 类型才是最新最全的 Props 清单。不确定时按以下顺序查阅基础布局 Propsstatic/app/components/core/layout/container.tsxFlex 专属 Propsstatic/app/components/core/layout/flex.tsxGrid 专属 Propsstatic/app/components/core/layout/grid.tsxStack 专属 Propsstatic/app/components/core/layout/stack.tsxText Propsstatic/app/components/core/text/text.tsxHeading Propsstatic/app/components/core/text/heading.tsxToken 参考布局与排版 Props 的值大多不是自由字符串而是受约束的设计 Token。取值如下也可在 static/app/utils/theme 相关定义中交叉核对。间距 TokenSpaceSize用于gap、padding0、2xs、xs、sm、md、lg、xl、2xl、3xl多值写法md lg垂直 水平响应式写法{{xs: sm, md: lg}}边框 TokenBorderVariant用于borderPropsprimary、muted、accent、danger、promotion、success、warning圆角 TokenRadiusSize用于radiusProps0、2xs、xs、sm、md、lg、xl、2xl、full字号 TokenTextSizexs \| sm \| md \| lg \| xl \| 2xlHeadingSizexs \| sm \| md \| lg \| xl \| 2xl \| 3xl \| 4xl表面背景TokenSurfaceVariant用于布局组件的backgroundPropsprimary、secondary、tertiary注意 container.tsx 中类型为ExcludeSurfaceVariant, overlay即不支持overlay内容色 TokenContentVariant用于 Text / Heading 的variantPropsContentVariantprimary \| secondary \| accent \| danger \| promotion \| success \| warning外加mutedText 和 Heading 在 ContentVariant 之外还额外接受muted其他核心组件规范Avatars应使用核心头像组件UserAvatar/、TeamAvatar/、ProjectAvatar/、OrganizationAvatar/、SentryAppAvatar/、DocIntegrationAvatar/它们统一从 static/app/components/core/avatar 暴露多个头像并列时使用AvatarList。不要使用裸img或写死静态路径去手工拼头像// ✅ 使用 Avatar 组件 useUser import {UserAvatar} from sentry/scraps/avatar; import {useUser} from sentry/utils/useUser; UserAvatar user{user} // ❌ 不要用裸 img 与静态路径 function Component() { return ( img src/path/to/image.jpg style{{ border, width: 20, height: 20, borderRadius: 50%, objectFit: cover, display: inline-block, }} / ); }Disclosure使用核心Disclosure组件而非自建展开/收起交互。对应实现位于 static/app/components/core/disclosure// ✅ 使用 Disclosure 组件 Disclosure Disclosure.TitleTitle/Disclosure.Title Disclosure.ContentContent that is toggled based on expanded state/Disclosure.Content /Disclosure; // ❌ 不要手工重写 disclosure 交互 function Component() { const [isExpanded, setIsExpanded] useState(false); return ( div Button onClick{() setIsExpanded(!isExpanded)} icon{IconChevron direction{isExpanded ? down : right} /} Title /Button {isExpanded ( ContainerContent that is toggled based on expanded state/Container )} /div ); }图标与图片图标所有图标必须放在static/app/icons目录绝不内联 SVG也不要放进其它目录。SVG 需用 svgo / svgomg 优化。// ❌ 绝不内联 SVG function Component() { return ( Button icon{ svg viewbox0 0 16 16} {/* ❌ 路径精度过高需用 SVGO 优化 */} circle cx8.00134 cy8.4314 r5.751412 / circle cx8.00134 cy8.4314 r12.751412 / line x18.41334 y15.255361 x28 y28.255421 / /svg /Button ) } // ❌ 不要把 SVG 放到 icons 目录之外 import {CustomIcon} from ./customIcon // ✅ 从图标集中导入 import {IconExclamation} from sentry/icons图片所有图片归属于static/app/images必须通过 webpack loader即sentry-images别名导入禁止使用静态路径引用// ✅ 通过 sentry-images 别名导入 import image from sentry-images/example.png; function Component() { return Image src{image} /; } // ❌ 所有图片都必须用 webpack loader 导入 function Component() { return Image src/path/to/image.png /; }快速自查清单在创建 styled component 之前逐项自问✅ 我能否用Flex、Grid或Stack完成布局✅ 纵向布局是否能用默认 column 方向的Stack✅ 边框 / 内边距 / 定位是否能用Container✅ 排版是否能用Text或Heading✅ 是否能用InfoTip或InfoText取代Tooltip✅ 是否能用响应式 Props 取代媒体查询✅ 是否能用gap取代 margin✅ 我需要的 Props 原语是否已支持查阅实现文件只要以上任何一项答案为是就使用核心原语。参考文件导航技能原文档.agents/skills/design-system/SKILL.md布局原语实现static/app/components/core/layout/index.tsx、container.tsx、flex.tsx、grid.tsx、stack.tsx排版原语实现static/app/components/core/text/index.tsx、text.tsx、heading.tsx帮助组件实现static/app/components/core/info/index.tsx运行时提供者static/app/scrapsProviders/index.tsx【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表