frontend-style-guide

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Lightdash Frontend Style Guide

Lightdash前端样式指南

Apply these rules when working on any frontend component in
packages/frontend/
.
packages/frontend/
中开发任何前端组件时,请遵循以下规则。

Mantine 8 Migration

Mantine 8迁移

CRITICAL: We are migrating from Mantine 6 to 8. Always upgrade v6 components when you encounter them.
重要提示:我们正在从Mantine 6迁移至8。遇到v6组件时,请务必将其升级。

Component Checklist

组件检查清单

When creating/updating components:
  • Use
    @mantine-8/core
    imports
  • No
    style
    or
    styles
    or
    sx
    props
  • Check Mantine docs/types for available component props
  • Use inline-style component props for styling when available (and follow <=3 props rule)
  • Use CSS modules when component props aren't available or when more than 3 inline-style props are needed
  • Theme values ('md', 'lg', 'xl', or 'ldGray.1', 'ldGray.2', 'ldDark.1', 'ldDark.2', etc) instead of magic numbers
  • When using mantine colors in css modules, always use the theme awared variables:
    • --mantine-color-${color}-text
      : for text on filled background
    • --mantine-color-${color}-filled
      : for filled background (strong color)
    • --mantine-color-${color}-filled-hover
      : for filled background on hover
    • --mantine-color-${color}-light
      : for light background
    • --mantine-color-${color}-light-hover
      : for light background on hover (light color)
    • --mantine-color-${color}-light-color
      : for text on light background
    • --mantine-color-${color}-outline
      : for outlines
    • --mantine-color-${color}-outline-hover
      : for outlines on hover
创建/更新组件时:
  • 使用
    @mantine-8/core
    导入
  • 禁止使用
    style
    styles
    sx
    属性
  • 查阅Mantine文档/类型以了解可用的组件属性
  • 可用时使用内联样式组件属性进行样式设置(并遵循≤3个属性规则)
  • 当组件属性无法满足需求或需要超过3个内联样式属性时,使用CSS modules
  • 使用主题值(如'md'、'lg'、'xl',或'ldGray.1'、'ldGray.2'、'ldDark.1'、'ldDark.2'等)而非魔法数字
  • 在CSS modules中使用Mantine颜色时,请始终使用主题感知变量:
    • --mantine-color-${color}-text
      :用于填充背景上的文本
    • --mantine-color-${color}-filled
      :用于填充背景(纯色)
    • --mantine-color-${color}-filled-hover
      :用于悬停时的填充背景
    • --mantine-color-${color}-light
      :用于浅色背景
    • --mantine-color-${color}-light-hover
      :用于悬停时的浅色背景
    • --mantine-color-${color}-light-color
      :用于浅色背景上的文本
    • --mantine-color-${color}-outline
      :用于轮廓
    • --mantine-color-${color}-outline-hover
      :用于悬停时的轮廓

Quick Migration Guide

快速迁移指南

tsx
// ❌ Mantine 6
import { Button, Group } from '@mantine/core';

<Group spacing="xs" noWrap>
    <Button sx={{ mt: 20 }}>Click</Button>
</Group>;

// ✅ Mantine 8
import { Button, Group } from '@mantine-8/core';

<Group gap="xs" wrap="nowrap">
    <Button mt={20}>Click</Button>
</Group>;
tsx
// ❌ Mantine 6
import { Button, Group } from '@mantine/core';

<Group spacing="xs" noWrap>
    <Button sx={{ mt: 20 }}>Click</Button>
</Group>;

// ✅ Mantine 8
import { Button, Group } from '@mantine-8/core';

<Group gap="xs" wrap="nowrap">
    <Button mt={20}>Click</Button>
</Group>;

Key Prop Changes

核心属性变更

  • spacing
    gap
  • noWrap
    wrap="nowrap"
  • sx
    → Component props (e.g.,
    mt
    ,
    w
    ,
    c
    ) or CSS modules
  • leftIcon
    leftSection
  • rightIcon
    rightSection
  • spacing
    gap
  • noWrap
    wrap="nowrap"
  • sx
    → 组件属性(如
    mt
    w
    c
    )或CSS modules
  • leftIcon
    leftSection
  • rightIcon
    rightSection

Styling Best Practices

样式最佳实践

Core Principle: Theme First

核心原则:优先使用主题

The goal is to use theme defaults whenever possible. Style overrides should be the exception, not the rule.
目标是尽可能使用主题默认值。样式覆写应作为例外,而非常规操作。

Styling Hierarchy

样式层级

  1. Best: No custom styles (use theme defaults)
  2. Theme extension: For repeated patterns, add to
    mantine8Theme.ts
  3. Component props: Simple overrides (1-3 props like
    mt="xl" w={240}
    )
  4. CSS modules: Complex styling or more than 3 props
  1. 最佳选择:不使用自定义样式(使用主题默认值)
  2. 主题扩展:对于重复样式模式,添加至
    mantine8Theme.ts
  3. 组件属性:简单覆写(1-3个属性,如
    mt="xl" w={240}
  4. CSS modules:复杂样式或超过3个属性时使用

NEVER Use

禁止使用

  • styles
    prop (always use CSS modules instead)
  • sx
    prop (it's a v6 prop)
  • style
    prop (inline styles)
  • styles
    属性(请始终使用CSS modules替代)
  • sx
    属性(属于v6属性)
  • style
    属性(内联样式)

Theme Extensions (For Repeated Patterns)

主题扩展(针对重复样式模式)

If you find yourself applying the same style override multiple times, add it to the theme in
mantine8Theme.ts
:
tsx
// In src/mantine8Theme.ts - inside the components object
components: {
    Button: Button.extend({
        styles: {
            root: {
                minWidth: '120px',
                fontWeight: 600,
            }
        }
    }),
}
如果发现自己多次应用相同的样式覆写,请将其添加至
mantine8Theme.ts
中的主题配置:
tsx
// 在src/mantine8Theme.ts的components对象内
components: {
    Button: Button.extend({
        styles: {
            root: {
                minWidth: '120px',
                fontWeight: 600,
            }
        }
    }),
}

Context-Specific Overrides

特定上下文覆写

Inline-style Component Props (1-3 simple props)

内联样式组件属性(1-3个简单属性)

tsx
// ✅ Good
<Button mt="xl" w={240} c="blue.6">Submit</Button>

// ❌ Bad - Too many props, use CSS modules instead
<Button mt={20} mb={20} ml={10} mr={10} w={240} c="blue.6" bg="white">Submit</Button>
Common inline-style props:
  • Layout:
    mt
    ,
    mb
    ,
    ml
    ,
    mr
    ,
    m
    ,
    p
    ,
    pt
    ,
    pb
    ,
    pl
    ,
    pr
  • Sizing:
    w
    ,
    h
    ,
    maw
    ,
    mah
    ,
    miw
    ,
    mih
  • Colors:
    c
    (color),
    bg
    (background)
  • Font:
    ff
    ,
    fs
    ,
    fw
  • Text:
    ta
    ,
    lh
tsx
// ✅ 推荐
<Button mt="xl" w={240} c="blue.6">Submit</Button>

// ❌ 不推荐 - 属性过多,请使用CSS modules替代
<Button mt={20} mb={20} ml={10} mr={10} w={240} c="blue.6" bg="white">Submit</Button>
常用内联样式属性:
  • 布局:
    mt
    ,
    mb
    ,
    ml
    ,
    mr
    ,
    m
    ,
    p
    ,
    pt
    ,
    pb
    ,
    pl
    ,
    pr
  • 尺寸:
    w
    ,
    h
    ,
    maw
    ,
    mah
    ,
    miw
    ,
    mih
  • 颜色:
    c
    (文本颜色),
    bg
    (背景色)
  • 字体:
    ff
    ,
    fs
    ,
    fw
  • 文本:
    ta
    ,
    lh

CSS Modules (complex styles or >3 props)

CSS modules(复杂样式或>3个属性)

Create a
.module.css
file in the same folder as the component:
css
/* Component.module.css */
.customCard {
    transition: transform 0.2s ease;
    cursor: pointer;
}

.customCard:hover {
    transform: translateY(-2px);
    box-shadow: var(--mantine-shadow-lg);
}
tsx
import styles from './Component.module.css';

<Card className={styles.customCard}>{/* content */}</Card>;
Do NOT include
.css.d.ts
files
- Vite handles this automatically.
在组件所在文件夹中创建
.module.css
文件:
css
/* Component.module.css */
.customCard {
    transition: transform 0.2s ease;
    cursor: pointer;
}

.customCard:hover {
    transform: translateY(-2px);
    box-shadow: var(--mantine-shadow-lg);
}
tsx
import styles from './Component.module.css';

<Card className={styles.customCard}>{/* content */}</Card>;
请勿包含
.css.d.ts
文件
- Vite会自动处理此内容。

Color Guidelines

颜色指南

Prefer default component colors - Mantine handles theme switching automatically.
When you need custom colors, use our custom scales for dark mode compatibility:
tsx
// ❌ Bad - Standard Mantine colors (poor dark mode support)
<Text c="gray.6">Secondary text</Text>

// ✅ Good - ldGray for borders and neutral elements
<Text c="ldGray.6">Secondary text</Text>

// ✅ Good - ldDark for elements that appear dark in light mode
<Button bg="ldDark.8" c="ldDark.0">Dark button</Button>

// ✅ Good - Foreground/background variables
<Text c="foreground">Primary text</Text>
<Box bg="background">Main background</Box>
优先使用组件默认颜色 - Mantine会自动处理主题切换。
当需要自定义颜色时,请使用我们的自定义色阶以支持深色模式:
tsx
// ❌ 不推荐 - 标准Mantine颜色(深色模式支持较差)
<Text c="gray.6">次要文本</Text>

// ✅ 推荐 - ldGray用于边框和中性元素
<Text c="ldGray.6">次要文本</Text>

// ✅ 推荐 - ldDark用于浅色模式下的深色背景元素
<Button bg="ldDark.8" c="ldDark.0">深色按钮</Button>

// ✅ 推荐 - 前景/背景变量
<Text c="foreground">主要文本</Text>
<Box bg="background">主背景</Box>

Custom Color Scales

自定义色阶

TokenPurpose
ldGray.0-9
Borders, subtle text, neutral UI elements
ldDark.0-9
Buttons/badges with dark backgrounds in light mode
background
Page/card backgrounds
foreground
Primary text color
令牌用途
ldGray.0-9
边框、次要文本、中性UI元素
ldDark.0-9
浅色模式下带有深色背景的按钮/徽章
background
页面/卡片背景
foreground
主要文本颜色

Dark Mode in CSS Modules

CSS modules中的深色模式

Use
@mixin dark
for theme-specific overrides:
css
.clickableRow {
    &:hover {
        background-color: var(--mantine-color-ldGray-0);

        @mixin dark {
            background-color: var(--mantine-color-ldDark-5);
        }
    }
}
Alternative: use CSS
light-dark()
function for single-line theme switching:
css
.clickableRow:hover {
    background-color: light-dark(
        var(--mantine-color-ldGray-0),
        var(--mantine-color-ldDark-5)
    );
}
使用
@mixin dark
进行主题特定覆写:
css
.clickableRow {
    &:hover {
        background-color: var(--mantine-color-ldGray-0);

        @mixin dark {
            background-color: var(--mantine-color-ldDark-5);
        }
    }
}
替代方案:使用CSS
light-dark()
函数实现单行主题切换:
css
.clickableRow:hover {
    background-color: light-dark(
        var(--mantine-color-ldGray-0),
        var(--mantine-color-ldDark-5)
    );
}

Always Use Theme Tokens

始终使用主题令牌

tsx
// ❌ Bad - Magic numbers
<Box p={16} mt={24}>

// ✅ Good - Theme tokens
<Box p="md" mt="lg">
tsx
// ❌ 不推荐 - 魔法数字
<Box p={16} mt={24}>

// ✅ 推荐 - 主题令牌
<Box p="md" mt="lg">

Beware of dependencies

注意依赖关系

If a component is migrated to use Mantine 8 Menu.Item, ensure its parent also uses Mantine 8 Menu
如果某个组件已迁移为使用Mantine 8的Menu.Item,请确保其父组件也使用Mantine 8的Menu

Remove Dead Styles

移除无用样式

Before moving styles to CSS modules, check if they're actually needed:
tsx
// ❌ Unnecessary - display: block has no effect on flex children
<Flex justify="flex-end">
    <Button style={{display: 'block'}}>Submit</Button>
</Flex>

// ✅ Better - Remove the style entirely
<Flex justify="flex-end">
    <Button>Submit</Button>
</Flex>
在将样式迁移至CSS modules之前,请检查这些样式是否真的有必要:
tsx
// ❌ 不必要 - display: block在flex子元素上无效
<Flex justify="flex-end">
    <Button style={{display: 'block'}}>Submit</Button>
</Flex>

// ✅ 优化 - 完全移除该样式
<Flex justify="flex-end">
    <Button>Submit</Button>
</Flex>

Theme-Aware Component Logic

主题感知组件逻辑

For JavaScript logic that needs to know the current theme:
tsx
import { useMantineColorScheme } from '@mantine/core';

const MyComponent = () => {
    const { colorScheme } = useMantineColorScheme();
    const iconColor = colorScheme === 'dark' ? 'blue.4' : 'blue.6';
    // ...
};
对于需要了解当前主题的JavaScript逻辑:
tsx
import { useMantineColorScheme } from '@mantine/core';

const MyComponent = () => {
    const { colorScheme } = useMantineColorScheme();
    const iconColor = colorScheme === 'dark' ? 'blue.4' : 'blue.6';
    // ...
};

Keep using mantine/core's clsx utility until we migrate to Mantine 8 fully

在完全迁移至Mantine 8之前,请继续使用mantine/core的clsx工具

tsx
import { clsx } from '@mantine/core';

const MyComponent = () => {
    return (
        <div className={clsx('my-class', 'my-other-class')}>My Component</div>
    );
};
tsx
import { clsx } from '@mantine/core';

const MyComponent = () => {
    return (
        <div className={clsx('my-class', 'my-other-class')}>My Component</div>
    );
};

Select/MultiSelect grouping has a different structure on Mantine 8

Mantine 8中Select/MultiSelect的分组结构有所不同

tsx
<Select
    label="Your favorite library"
    placeholder="Pick value"
    data={[
        { group: 'Frontend', items: ['React', 'Angular'] },
        { group: 'Backend', items: ['Express', 'Django'] },
    ]}
/>
tsx
<Select
    label="你最喜爱的库"
    placeholder="选择值"
    data={[
        { group: '前端', items: ['React', 'Angular'] },
        { group: '后端', items: ['Express', 'Django'] },
    ]}
/>

Reusable Components

可复用组件

Modals

模态框

  • Always use
    MantineModal
    from
    components/common/MantineModal
    - never use Mantine's Modal directly
  • See
    stories/Modal.stories.tsx
    for usage examples
  • For forms inside modals: use
    id
    on the form and
    form="form-id"
    on the submit button
  • For alerts inside modals: use
    Callout
    with variants
    danger
    ,
    warning
    ,
    info
  • 请始终使用
    components/common/MantineModal
    中的
    MantineModal
    - 切勿直接使用Mantine的Modal
  • 用法示例请查看
    stories/Modal.stories.tsx
  • 模态框内的表单:为表单添加
    id
    ,并在提交按钮上使用
    form="form-id"
  • 模态框内的提示:使用
    Callout
    组件,变体包括
    danger
    warning
    info

Callouts

提示框

  • Use
    Callout
    from
    components/common/Callout
  • Variants:
    danger
    ,
    warning
    ,
    info
  • 使用
    components/common/Callout
    中的
    Callout
    组件
  • 变体:
    danger
    warning
    info

Polymorphic Clickable Containers

多态可点击容器

Use these when you need a layout container that is also clickable — avoids the native
<button>
background/border reset problem.
  • PolymorphicGroupButton
    from
    components/common/PolymorphicGroupButton
    — a
    Group
    (flex row) that is polymorphic and sets
    cursor: pointer
    . Use for horizontal groups of elements that act as a single button.
  • PolymorphicPaperButton
    from
    components/common/PolymorphicPaperButton
    — a
    Paper
    (card surface) that is polymorphic and sets
    cursor: pointer
    . Use for card-like clickable surfaces.
Both accept all props of their base component (
GroupProps
/
PaperProps
) plus a
component
prop for the underlying element.
tsx
// ✅ Clickable row without native button style bleed
<PolymorphicGroupButton component="div" gap="sm" onClick={handleClick}>
    <MantineIcon icon={IconFolder} />
    <Text>Label</Text>
</PolymorphicGroupButton>

// ✅ Clickable card surface
<PolymorphicPaperButton component="div" p="md" onClick={handleClick}>
    Card content
</PolymorphicPaperButton>

// ❌ Avoid - native <button> brings unwanted background/border in menus and panels
<UnstyledButton>
    <Group>...</Group>
</UnstyledButton>
当你需要一个同时可点击的布局容器时,请使用以下组件 — 避免原生
<button>
的背景/边框重置问题。
  • components/common/PolymorphicGroupButton
    中的
    PolymorphicGroupButton
    — 一个多态的
    Group
    (弹性行),设置了
    cursor: pointer
    。用于可作为单个按钮的水平元素组。
  • components/common/PolymorphicPaperButton
    中的
    PolymorphicPaperButton
    — 一个多态的
    Paper
    (卡片表面),设置了
    cursor: pointer
    。用于卡片式可点击表面。
两者均接受其基础组件的所有属性(
GroupProps
/
PaperProps
),以及用于设置底层元素的
component
属性。
tsx
// ✅ 无原生按钮样式渗透的可点击行
<PolymorphicGroupButton component="div" gap="sm" onClick={handleClick}>
    <MantineIcon icon={IconFolder} />
    <Text>标签</Text>
</PolymorphicGroupButton>

// ✅ 可点击卡片表面
<PolymorphicPaperButton component="div" p="md" onClick={handleClick}>
    卡片内容
</PolymorphicPaperButton>

// ❌ 避免使用 - 原生<button>会在菜单和面板中带来不必要的背景/边框
<UnstyledButton>
    <Group>...</Group>
</UnstyledButton>

EmptyStateLoader

EmptyStateLoader

  • Use
    EmptyStateLoader
    from
    components/common/EmptyStateLoader
    for any centered loading state: page-level guards, panels, tables, empty containers
  • Built on
    SuboptimalState
    (Mantine v8) — renders a spinner with an optional title, fully centered in its parent
  • 任何居中加载状态(页面级守卫、面板、表格、空容器)都请使用
    components/common/EmptyStateLoader
    中的
    EmptyStateLoader
  • 基于
    SuboptimalState
    (Mantine v8)构建 — 渲染带可选标题的加载器,并在父容器中完全居中

TruncatedText

TruncatedText

  • Use
    TruncatedText
    from
    components/common/TruncatedText
    whenever text may overflow a constrained width
  • Pass
    maxWidth
    (number or string) to control the truncation boundary
  • Automatically shows a tooltip with the full text only when the text is actually truncated (no tooltip spam for short names)
  • Defaults to
    fz="sm"
    ; override via standard
    Text
    props
tsx
// ✅ Good - truncates long names, tooltip only appears when needed
<TruncatedText maxWidth={200}>{item.name}</TruncatedText>

// ✅ Accepts any Text prop
<TruncatedText maxWidth="100%" fw={500}>{space.name}</TruncatedText>
  • 当文本可能超出限制宽度时,请使用
    components/common/TruncatedText
    中的
    TruncatedText
    组件
  • 传递
    maxWidth
    (数字或字符串)以控制截断边界
  • 仅当文本确实被截断时才自动显示包含完整文本的提示框(避免短文本的提示框干扰)
  • 默认字体大小为
    fz="sm"
    ;可通过标准
    Text
    属性覆盖
tsx
// ✅ 推荐 - 截断长名称,仅在需要时显示提示框
<TruncatedText maxWidth={200}>{item.name}</TruncatedText>

// ✅ 支持任何Text属性
<TruncatedText maxWidth="100%" fw={500}>{space.name}</TruncatedText>

Mantine Documentation

Mantine文档

List of all components and links to their documentation in LLM-friendly format:
https://mantine.dev/llms.txt
所有组件列表及其LLM友好格式的文档链接:
https://mantine.dev/llms.txt