frontend-style-guide
npx skills add https://github.com/lightdash/lightdash --skill frontend-style-guide
Agent 安装分布
Skill 文档
Lightdash Frontend Style Guide
Apply these rules when working on any frontend component in packages/frontend/.
Mantine 8 Migration
CRITICAL: We are migrating from Mantine 6 to 8. Always upgrade v6 components when you encounter them.
Component Checklist
When creating/updating components:
- Use
@mantine-8/coreimports - No
styleorstylesorsxprops - 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
Quick Migration Guide
// â 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âgapnoWrapâwrap="nowrap"sxâ Component props (e.g.,mt,w,c) or CSS modulesleftIconâleftSectionrightIconâ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
- Best: No custom styles (use theme defaults)
- Theme extension: For repeated patterns, add to
mantine8Theme.ts - Component props: Simple overrides (1-3 props like
mt="xl" w={240}) - CSS modules: Complex styling or more than 3 props
NEVER Use
stylesprop (always use CSS modules instead)sxprop (it’s a v6 prop)styleprop (inline styles)
Theme Extensions (For Repeated Patterns)
If you find yourself applying the same style override multiple times, add it to the theme in mantine8Theme.ts:
// In src/mantine8Theme.ts - inside the components object
components: {
Button: Button.extend({
styles: {
root: {
minWidth: '120px',
fontWeight: 600,
}
}
}),
}
Context-Specific Overrides
Inline-style Component Props (1-3 simple props)
// â
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
CSS Modules (complex styles or >3 props)
Create a .module.css file in the same folder as the component:
/* Component.module.css */
.customCard {
transition: transform 0.2s ease;
cursor: pointer;
}
.customCard:hover {
transform: translateY(-2px);
box-shadow: var(--mantine-shadow-lg);
}
import styles from './Component.module.css';
<Card className={styles.customCard}>{/* content */}</Card>;
Do NOT include .css.d.ts files – Vite handles this automatically.
Color Guidelines
Prefer default component colors – Mantine handles theme switching automatically.
When you need custom colors, use our custom scales for dark mode compatibility:
// â 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>
Custom Color Scales
| Token | Purpose |
|---|---|
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 |
Dark Mode in CSS Modules
Use @mixin dark for theme-specific overrides:
.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:
.clickableRow:hover {
background-color: light-dark(
var(--mantine-color-ldGray-0),
var(--mantine-color-ldDark-5)
);
}
Always Use Theme Tokens
// â Bad - Magic numbers
<Box p={16} mt={24}>
// â
Good - Theme tokens
<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
Remove Dead Styles
Before moving styles to CSS modules, check if they’re actually needed:
// â 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>
Theme-Aware Component Logic
For JavaScript logic that needs to know the current theme:
import { useMantineColorScheme } from '@mantine/core';
const MyComponent = () => {
const { colorScheme } = useMantineColorScheme();
const iconColor = colorScheme === 'dark' ? 'blue.4' : 'blue.6';
// ...
};
Reusable Components
Modals
- Always use
MantineModalfromcomponents/common/MantineModal– never use Mantine’s Modal directly - See
stories/Modal.stories.tsxfor usage examples - For forms inside modals: use
idon the form andform="form-id"on the submit button - For alerts inside modals: use
Calloutwith variantsdanger,warning,info
Callouts
- Use
Calloutfromcomponents/common/Callout - Variants:
danger,warning,info
Mantine Documentation
List of all components and links to their documentation in LLM-friendly format: https://mantine.dev/llms.txt