minimal-style-guide
npx skills add https://github.com/sweetretry/skills --skill minimal-style-guide
Agent 安装分布
Skill 文档
å¿«éåè
| è¦ç´ | è§è |
|---|---|
| çç½ç | æ¡é¢ 40%+, ç§»å¨ç«¯ 15-20% |
| åè§ | rounded-md (6px) æ rounded-lg (8px) |
| èæ¯è² | bg-background (CSS Variable å±å®ä¹çº¯ç½/è¿é») |
| åæ¯è² | text-foreground (CSS Variable å±å®ä¹è¿é»/è¿ç½) |
| 强è°è² | text-primary / bg-primary (å
¨å±ä»
1 ç§) |
| Touch Target | â¥44px |
| 卿弿 | Framer Motion, Spring Physics ä¼å |
| è§è§æ¨¡å¼ | åæ³è¡¨é¢ / æçæ¯ä¾ / Bento ç½æ ¼ / åå²çº¿èå¥ / æ¸éæç¤º (å¯ç»å) |
| 对æ¯åº¦ | WCAG AA (â¥4.5:1)ï¼OLED å®å ¨æè² |
| 硬ç¼ç Hex | ç¦æ¢ â ç»ä»¶å±ä¸¥æ ¼ä½¿ç¨è¯ä¹ Token |
æ ¸å¿æä»¤ (Core Mandate)
æå»º 2026 èå¼ç “æºè½æç® (Smart Simplicity)” ââ å©ç¨æ°å¦ç§©åºå AI éåºæ§ç®¡ç认ç¥è´è·ã
å¼ºå¶ææ¯æ :
- UI åº:
shadcn/ui(ç§»é¤ææé»è®¤è£ é¥°æ§ææ) - æ ·å¼å¼æ:
Tailwind CSS(ä¸¥æ ¼ä½¿ç¨è¯ä¹å Token ç±»ï¼å¨ CSS Variables å±å®ä¹æç®è²å½©) - 卿åº:
Framer Motion(ä» ç¨äºæ¨¡æç©çä¸ççå¿ ç¶æ§)
ä¸ã设计å²å¦
- ä¿¡åªæ¯æå¤§å: æ¯ä¸ªå ç´ å¿ é¡»æä¸ä» æä¸ä¸ªæç¡®åè½ãå é¤åä¸å½±ååè½çå ç´ å¿ é¡»å é¤ã
- çç½å³å 容: çç½æ¯ä¸»å¨ç设计å ç´ ï¼ç¨äºæå»ºè§çº¿æµåå±çº§ã
- æçå³çé¢: åä½çåéãåå·åè¡é«æ¿æ 90% çç»æååä»»å¡ã
- è¯å®ç设计: æç»æç©ãæç»è£ é¥°æ§æè´¨ï¼æè´¨å¿ é¡»æå¡äºå±çº§ã
äºãåºç¡ç³»ç»
1. Token åè²å½©ç³»ç» (Minimal Color Tokens)
ååï¼ ææè²å½©å¨ CSS Variables å±å®ä¹ï¼ç»ä»¶å±ä¸¥æ ¼ä½¿ç¨ Tailwind è¯ä¹ç±»ãç¦æ¢å¨ç»ä»¶ä»£ç ä¸åºç°ä»»ä½ç¡¬ç¼ç Hex å¼ã
CSS Variables å®ä¹ï¼æç®æ 彩è²è°ï¼oklch è²å½©ç©ºé´ï¼ï¼
:root {
/* æç® Light â 纯ç½åº + è¿é»åæ¯ + æ 彩 Primary */
--background: oklch(1 0 0); /* çº¯ç½ */
--foreground: oklch(0.145 0 0); /* è¿é» */
--card: oklch(0.985 0 0); /* æå¾®ç°ç½ */
--card-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0); /* æ å½©ä¸»è² â åè²ä¸»å¯¼ */
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--border: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--destructive: oklch(0.577 0.245 27.325);
}
.dark {
/* æç® Dark â OLED å®å
¨è¿é» (é纯é») + é«äº®åæ¯ */
--background: oklch(0.145 0 0); /* â #1A1A1A, OLED å®å
¨ */
--foreground: oklch(0.985 0 0); /* è¿ç½åæ¯ */
--card: oklch(0.205 0 0); /* å¡çå¾®æäº® */
--card-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0); /* å转æ å½©ä¸»è² */
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--border: oklch(0.269 0 0);
--ring: oklch(0.556 0 0);
--destructive: oklch(0.704 0.191 22.216);
}
ç»ä»¶å±ç¨æ³ç¤ºä¾ï¼
// â
æ£ç¡® â 使ç¨è¯ä¹ Token
<div className="bg-background text-foreground">
<div className="bg-card border-border">
<span className="text-muted-foreground">
// â ç¦æ¢ â 硬ç¼ç Hex
<div className="bg-[#121212] text-[#F9FAFB]">
<div style={{ background: '#1A1A1A' }}>
2. 空é´ä¸å¼å¸
- çç½ç: æ¡é¢ç«¯ 40%+ï¼ç§»å¨ç«¯ Compact Mode 15-20%
- é´è·ç³»ç»: 4px åºå
- Tight:
4px,8px(ç»ä»¶å é¨) - Loose:
32px,64px,128px(åºååå²)
- Tight:
- ç§»å¨ç«¯éé
:
<768pxæ¶ææ Loose é´è·æå (my-32âmy-16)ï¼Touch Target â¥44px - Bento Grid: 4å/6åï¼Gap
16-24pxï¼Radius12-24px
3. å ä½ä¸å½¢æ
- æå½¢è¾¹ç: å
容ä¸è½å¨çç©ºä¸æ¼æµ®
- çç½ >128px æ¶å¼å
¥ Surface Tones (
bg-secondary/30) æ Micro-Borders (border-border/40) - Section ä¹é´ä½¿ç¨æç»åå²çº¿ (
h-px bg-border/20)
- çç½ >128px æ¶å¼å
¥ Surface Tones (
- åè§: ç»ä¸ä½¿ç¨
rounded-md(6px) ærounded-lg(8px)ï¼ä» Primary Button/Tag å¯ç¨å ¨åè§
4. è²å½©åå
- åè²ä¸»å¯¼: çé¢ 90% 为æ 彩è²ï¼éè¿
bg-backgroundãbg-cardãbg-mutedç Token 表达ç°é¶å±çº§ - åä¸å¼ºè°è²: å
¨å±ä»
ä¸ç§åçè²ï¼éè¿
--primaryå®ä¹ï¼ï¼ä» ç¨äº Primary Action æ Active State - æç®åé¦: æç»é«é¥±ååº¦èæ¯è²åï¼ä»
ä½¿ç¨ æååè² (
text-primary) æ 1px ä¾§è¾¹æç¤ºæ¡ (border-l-2 border-primary) - Dark Mode: èæ¯ä½¿ç¨ OLED å®å
¨çè¿é»è² (
--background: oklch(0.145 0 0))ï¼ç¦æ¢çº¯é»
5. æçä½ç³»
- åä½:
Inter,San Francisco,Helvetica Now - å±çº§:
- H1/Display: æå¤§åå· + æç»/æç²åéå½¢æå¼ºå¯¹æ¯
- Body: è¡é«
1.6-1.8 - Micro:
text-muted-foreground
- æ éç¢: ææææ¬å¯¹æ¯åº¦å¿ 须符å WCAG AA (>4.5:1)
ä¸ãè§è§æ¨¡å¼èå (Visual Mode Palette)
以䏿¨¡å¼å¯ç¬ç«ä½¿ç¨æç»åå å ï¼æ ¹æ®å 容类åéæ©æä½³æé ãé¿å å¨åä¸é¡¹ç®ä¸æ»¥ç¨æææ¨¡å¼ â éæ© 2-3 ç§å»ºç«ç»ä¸çè§è§è¯è¨ã
1. åæ³è¡¨é¢ (Subtractive Surface)
容å¨é»è®¤éæï¼ä» å¨çç½è¿å¤§æ¶æå°åå¼å ¥èæ¯ã
- å¡çé¦é
bg-transparent border-none shadow-noneï¼è®©å 容èªç¶å¼å¸ã - ä»
å½åºåé´è· >128px æ¶å¼å
¥
bg-secondary/30ä½ä¸ºå¾®å¼±è¡¨é¢æ è®°ã - éè¿æçåé´è·å»ºç«å±çº§ï¼èéèæ¯è²ååºã
- éç¨ï¼ å客æç« ãå ³äºé¡µãå 容为主çè½å°é¡µã
2. æçæ¯ä¾ (Typographic Scale)
è¶ å¤§æ é¢ + æç«¯åé对æ¯ä½ä¸ºä¸»è§è§å ç´ ï¼æ¿ä»£å¾å½¢è£ 饰ã
- Display æ é¢ä½¿ç¨
text-6xl md:text-8xl font-extralight tracking-tightåé æå§æ§ã - 坿 é¢ä½¿ç¨
font-boldå½¢æåé两æå¯¹æ¯ã - æ 颿¬èº«å³æ¯è®¾è®¡å ç´ ï¼æ éé¢å¤å¾å½¢æè²å½©ä¿®é¥°ã
- éç¨ï¼ 个人ç½ç« Heroãæ¶å°åçãåºçç©å°é¢ã
3. Bento ç½æ ¼ (Bento Grid)
éå¯¹ç§°ç½æ ¼å¸å±ï¼éè¿ä¸çååºååé è§è§èå¥ã
- Hero å
col-span-2 row-span-2ï¼æé æ£æ¹åç«ç´å交æ¿ã - Gap 使ç¨
gap-4 md:gap-6ï¼ä¿æå¼å¸æã - æ¯ä¸ªå¡çå
é¨çç½å
è¶³ (
p-6 md:p-8)ï¼å 容ä¸è´´è¾¹ã - Mobile éå为åå (
grid-cols-1)ï¼é´è·æåã - éç¨ï¼ ä½åéãåè½å±ç¤ºã产åç©éµã
4. åå²çº¿èå¥ (Divider Rhythm)
åä¸çº§åå²çº¿åé åç´æ¹åçè§è§éµå¾ã
- 使ç¨
h-px bg-border/20å¨ Section é´åé æç»åå²ã - åå²çº¿é´è·ä¸åç â éè¦å 容åçç½æ´å¤§ï¼å»ºç«å±çº§ã
- åå²çº¿å¯æé
max-w-[120px]ç线æ¡ä½ä¸ºè£ é¥°æ§æç¹ã - éç¨ï¼ é¿é¡µé¢ã夿®µè½æç« ãæ¡ç®å表ã
5. æ¸éæç¤º (Fade Reveal)
æ»å¨è§¦åç纯éæåº¦è¿æ¸¡ï¼ä½ç§»å å¶å¨ â¤10pxã
- éè¿
useScroll+useTransform驱å¨opacityä» 0 â 1ã - åç´ä½ç§»ä¸¥æ ¼æ§å¶å¨
y: 10以å ï¼æç»å¤¸å¼ çæ»å ¥ææã - æ¯ä¸ª Section ç¬ç«è§¦åï¼ä½¿ç¨
whileInView+viewport={{ once: true }}ã - éç¨ï¼ å 容页æ¸è¿æç¤ºãFAQ å±å¼ãæ¶é´çº¿ã
åãç»ä»¶å®ç°
shadcn/ui æç®æ¹é (Minimal Overrides)
æææ¹é éè¿ cn() åå¹¶æ ·å¼ï¼ä½¿ç¨è¯ä¹ Tokenï¼ç¦æ¢ç¡¬ç¼ç é¢è²å¼ã
Button
// Primary â å
å¶çå®å¿æé®
<Button className={cn(
"bg-primary text-primary-foreground",
"rounded-md font-medium",
"shadow-none transition-none" // ç± Framer Motion æ¥ç®¡
)}>
<motion.span whileTap={{ scale: 0.97 }} transition={microMotion}>
Action
</motion.span>
</Button>
// Ghost â æç®é¦éï¼å¤§å¤æ°åºæ¯ä½¿ç¨ Ghost
<Button variant="ghost" className={cn(
"text-muted-foreground hover:text-foreground",
"hover:bg-transparent hover:opacity-80"
)}>
Secondary
</Button>
Card
// é»è®¤éæ â åæ³è¡¨é¢åå
<motion.div
className={cn(
"border-none shadow-none bg-transparent",
"rounded-lg"
)}
initial={{ opacity: 0, y: 8 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={layoutMotion}
>
<div className="p-6">
<h3 className="text-foreground text-lg font-semibold tracking-tight">
{title}
</h3>
<p className="text-muted-foreground text-sm mt-1">
{description}
</p>
</div>
</motion.div>
// æèæ¯åä½ â ä»
å¨çç½è¿å¤§æ¶ä½¿ç¨
<Card className={cn("bg-muted/30 border-none shadow-none rounded-lg")} />
Input
// åºçº¿æ ·å¼ â æç®é¦é
<div className="space-y-2">
<Label className="text-sm font-medium text-foreground">
{label}
</Label>
<Input className={cn(
"border-b border-border rounded-none bg-transparent",
"focus-visible:ring-0 focus-visible:border-foreground",
"placeholder:text-muted-foreground"
)} />
</div>
// å¡«å
æ ·å¼ â å¤é
<Input className={cn(
"bg-muted/20 border-none rounded-md",
"focus-visible:ring-1 focus-visible:ring-ring"
)} />
Dialog
<Dialog>
<DialogOverlay className={cn(
"bg-background/80 backdrop-blur-sm"
)} />
<DialogContent className={cn(
"bg-card border border-border rounded-lg",
"shadow-none"
)}>
<AnimatePresence mode="wait">
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }}
transition={microMotion}
>
{children}
</motion.div>
</AnimatePresence>
</DialogContent>
</Dialog>
Navigation
<nav className={cn(
"fixed top-0 w-full z-50",
"bg-background/80 backdrop-blur-sm border-b border-border/40"
)}>
<div className="flex items-center justify-between px-6 md:px-8 h-14">
<span className="text-foreground font-medium tracking-tight">
{brand}
</span>
<div className="flex gap-6 text-muted-foreground text-sm">
{links.map(link => (
<motion.a
key={link.href}
className="hover:text-foreground transition-colors"
whileHover={{ opacity: 0.7 }}
transition={microMotion}
>
{link.label}
</motion.a>
))}
</div>
</div>
</nav>
Bento Grid
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 md:gap-6">
{/* Hero â 主è§è§ç¦ç¹ */}
<motion.div
className="col-span-1 md:col-span-2 md:row-span-2 bg-muted/30 rounded-lg p-6 md:p-8"
initial={{ opacity: 0, y: 8 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={layoutMotion}
>
{heroContent}
</motion.div>
{/* æ£æ¹å */}
<motion.div
className="col-span-1 bg-muted/20 rounded-lg p-6 aspect-square"
initial={{ opacity: 0, y: 8 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ ...layoutMotion, delay: 0.05 }}
>
{squareContent}
</motion.div>
{/* ç«ç´å */}
<motion.div
className="col-span-1 bg-muted/20 rounded-lg p-6"
initial={{ opacity: 0, y: 8 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ ...layoutMotion, delay: 0.1 }}
>
{verticalContent}
</motion.div>
</div>
卿忰
// å¾®äº¤äº Spring â æé®ãæ¬åãå°å
ç´
const microMotion = {
type: "spring",
stiffness: 260,
damping: 20,
mass: 1
};
// å¸å±è¿æ¸¡ â Section å
¥åºãå¡çå±å¼
const layoutMotion = {
duration: 0.3,
ease: [0.16, 1, 0.3, 1] // Expo Out
};
// æ»å¨æç¤º â æ¸éå
¥åºï¼æå
å¶
const revealMotion = {
duration: 0.5,
ease: [0.16, 1, 0.3, 1]
};
æ»å¨é©±å¨å¨æ
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"]
});
const opacity = useTransform(scrollYProgress, [0, 0.3], [0, 1]);
const y = useTransform(scrollYProgress, [0, 0.3], [10, 0]);
å ¥åºç¼æ (Stagger Orchestration)
const container = {
animate: { transition: { staggerChildren: 0.06 } }
};
const item = {
initial: { opacity: 0, y: 8 },
animate: {
opacity: 1,
y: 0,
transition: revealMotion
}
};
äºã代ç å®¡æ¥æ¸ å (The Minimal Audit)
Token åè§
- Token Check â ç»ä»¶ä»£ç 䏿¯å¦åå¨ç¡¬ç¼ç Hex (å¦
#121212,#F9FAFB)ï¼ææé¢è²æ¯å¦ä½¿ç¨bg-background/text-foreground/border-borderçè¯ä¹ç±»ï¼ - CSS Variable Check â æç®è²è°æ¯å¦å¨
:root/.darkç CSS Variables ä¸ä½¿ç¨ oklch() æ£ç¡®å®ä¹ï¼
è§è§è´¨é (åæ³æ£éª)
- Structure Check â å 容æ¯å¦”æ¼æµ®”ï¼çç½ >128px æ¶æ¯å¦å¢å äº Surface Tone æ Dividerï¼
- Subtractive Check â è¿è½å é¤ä»ä¹èä¸å½±ååè½ï¼æ¯ä¸ªå ç´ æ¯å¦æä¸ä» æä¸ä¸ªåè½ï¼
- Whitespace Check â å°è¯å° Margin ç¿»åï¼æææ¯å¦æ´å¥½ï¼
- Color Constraint â å±å¹ä¸çé¢è²ç§ç±»ï¼ä¸å«ç°é¶ï¼æ¯å¦è¶ è¿ 1 ç§ï¼
- Depth Check â æ¯å¦å»é¤ææä¸å¿ è¦ç shadowï¼å®¹å¨æ¯å¦é»è®¤éæï¼
å¨æè´¨é
- Spring Check â 交äºå¨ææ¯å¦ä½¿ç¨ Spring Physicsï¼æ¯å¦åå¨çº¿æ§æ ease-in-out å¨ç»ï¼
- Displacement Check â ä½ç§»æ¯å¦å å¶å¨ â¤10pxï¼æ¯å¦åå¨å¤¸å¼ çæ»å ¥/弹跳ææï¼
- Duration Check â ææå¨ææ¶é¿æ¯å¦ <400msï¼
å¯è®¿é®æ§
- Contrast Safety â ææææ¬ (å°¤å
¶æ¯
text-muted-foreground) 对æ¯åº¦æ¯å¦ ⥠4.5:1ï¼ - Motion Safety â æ¯å¦å°é
prefers-reduced-motionï¼å¼å¯åæ¯å¦ç¦ç¨æ¸éæç¤ºå stagger å¨ç»ï¼ - OLED Safety â Dark Mode èæ¯æ¯å¦ä½¿ç¨
oklch(0.145 0 0)(â#1A1A1A)ï¼é¿å 纯é»ï¼ - Responsive Check â ç§»å¨ç«¯æ¯å¦å¼å¯ Compact Modeï¼Touch Target â¥44pxï¼é´è·æ¯å¦æåï¼
å ãä¸éç¨åºæ¯
- æ°æ®å¯éå Dashboardï¼ä½¿ç¨ swiss-style-guideï¼
- éè¦ä¸°å¯è§è§å±æ¬¡çåçè¥é页
- 游ææå¨±ä¹ç±»åºç¨
åæç¬è®° (Composition Notes)
æ¬èä¾
design-directoræè½å¨å¤é£æ ¼èååºæ¯ä¸ä½¿ç¨ãå®ä¹æ¬é£æ ¼æåä¸åªäºå±æ§æ¯ç¡¬çº¦æï¼å³ä½¿å¨æ··å模å¼ä¸ä¹ä¸å¯è¦çï¼ï¼åªäºæ¯è½¯çº¦æï¼å¯ç± Director æ ¹æ®é¡¹ç®éæ±è°æ´ï¼ã
硬约æ (Hard Constraints â ä¸å¯è¦ç)
| ID | 屿§ | è§èå¼ | çç± |
|---|---|---|---|
| M.H1 | Token å | ç¦æ¢ç¡¬ç¼ç Hexï¼ç»ä»¶å±ä½¿ç¨è¯ä¹ Token | è·¨é£æ ¼èåçåºç¡è®¾æ½ï¼ææé£æ ¼å ±äº«æ¤çº¦æ |
| M.H2 | è²å½©ç©ºé´ | oklch | æç¥ååè²å½©ç©ºé´ï¼æ··å项ç®çæ åï¼å·²å¨ CSS Variables å±å®ä¹ |
| M.H3 | åè²ä¸»å¯¼ | çé¢ 90% æ 彩è²ï¼å
¨å±ä»
1 ç§å¼ºè°è² (--primary) |
ä¿¡åªæ¯æå¤§åçæ ¸å¿å²å¦ââå¢å 强è°è²å°ç ´åæç®ç¹å¾ |
| M.H4 | 卿ä½ç§» | â¤10px | è¶ è¿æ¤éå¼å°å¤±å»”å å¶”ç¹å¾ï¼å¨æç¥ä¸å为 Expressive 飿 ¼ |
| M.H5 | 对æ¯åº¦ | WCAG AA (â¥4.5:1) | æ éç¢å¼ºå¶è¦æ± |
| M.H6 | OLED å®å ¨æè² | oklch(0.145 0 0) â #1A1A1Aï¼ç¦æ¢çº¯é» |
OLED æå½±é²æ¤ + æè²æ¨¡å¼çè§è§èé度 |
软约æ (Soft Constraints â å¯ç± Director è¦ç)
| ID | 屿§ | é»è®¤å¼ | Director å¯è°æ´èå´ | è§¦åæ¡ä»¶ |
|---|---|---|---|---|
| M.S1 | åè§ | rounded-md (6px) / rounded-lg (8px) |
å¯éè³ rounded-none (0px) æ rounded-sm (2px) |
å½ Swiss 为项ç®ä¸»é£æ ¼æ¶ï¼Minimal åºåå¯è·éç´è§ä»¥ä¿æå ¨å±å ä½ä¸è´æ§ |
| M.S2 | çç½ç | æ¡é¢ 40%+ | å¯éè³ 25â30% | å½ä¸ Swiss èå䏿´ä½ä¿¡æ¯å¯åº¦éæ±è¾é«æ¶ |
| M.S3 | 卿æ¶é¿ | <400ms | å¯ç¼©çè³ <300ms | å½ Swiss 为项ç®ä¸»é£æ ¼ï¼éè¦ç»ä¸å°æ´å¿«çåè½æ§è奿¶ |
| M.S4 | åæ³è¡¨é¢ | é»è®¤ bg-transparent border-none shadow-none |
å¯ä½¿ç¨ bg-card border-border |
å½åºåéè¦ä¸ Swiss è¾¹æ¡å®¹å¨è§è§è¡æ¥æ¶ï¼å¦åµå ¥å¨ Swiss å¸å±ä¸çå 容åºåï¼ |
| M.S5 | Bento ç½æ ¼ | 4â6 åçµæ´»å¸å± | å¯å¯¹é½å° 12 å 8pt ç½æ ¼ | å½ Swiss 为项ç®ä¸çä»»ä¸é£æ ¼ï¼éè¦å ¨å±ç½æ ¼åºçº¿å¯¹é½æ¶ |
| M.S6 | Stagger é´é | 0.06s | å¯ç¼©çè³ 0.03â0.04s | å½ Swiss ä¸ºä¸»é£æ ¼ï¼æ´ä½äº¤äºèå¥éè¦æ´å¿«æ¶ |