关于 grove-ui-design
Grove UI Design Skill 为 Grove 网站的页面设计和实现提供规范,适用于构建者与设计者。内容覆盖 Grove 术语切换、玻璃效果、季节主题、自然装饰及无障碍与移动端设计。
主要功能
- 01按模式切换 Grove 术语
- 02应用玻璃质感组件与样式
- 03使用四季配色和天气效果
- 04生成随机森林装饰
- 05遵循 Lucide 图标规范
适用场景
- 01设计 Grove 网站页面
- 02制作故事与路线图页面
- 03构建移动端导航
- 04为背景装饰添加可读内容层
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ autumnsgrove/ lattice --skill grove-ui-design安装后在 Claude Code 中输入/grove-ui-design确认已加载。
详细介绍
Grove UI 设计技能
何时启用
在以下情况启用此技能:
- 为 Grove 网站创建或改进页面
- 添加装饰性的自然元素(树木、云朵、天气效果)
- 实现玻璃拟态效果以提升可读性
- 使用季节主题系统
- 构建导航模式(导航栏、移动端溢出菜单)
- 创建引导用户浏览内容的“故事”页面
- 确保 UI 适配移动设备且无障碍
- 选择图标或视觉元素
Grove 美学
Grove 是一个地方。它以自然为主题,温暖而宜人,就像一家午夜营业、文档完善的茶馆。
核心原则
Warm, introspective, queer, unapologetically building something meaningful.
Write with the warmth of a midnight tea shop and the clarity of good documentation.
每个设计选择都应让人感到:
- 热情友好 — 像走进一处舒适的空间
- 自然有机 — 自然,不刻板,也不显得企业化
- 易于阅读 — 内容优先,装饰起到增强作用,绝不遮挡内容
- 充满生机 — 有细微动画、季节变化和随机效果
Grove 模式与术语(GroveTerm V2)
Grove 具有一个术语系统,可根据用户的 Grove 模式设置,在 Grove 风格术语和标准术语之间自动切换。始终使用 GroveTerm 组件,不要在 UI 中硬编码 Grove 术语。
新访客的 Grove 模式默认关闭。他们会看到熟悉的术语:“文章”而不是“花朵”,“控制面板”而不是“树屋”,“支持”而不是“门廊”。用户通过页脚切换开关选择启用后,便会看到完整的自然主题词汇及其交互式定义。
组件套件:
import {(GroveTerm, GroveSwap, GroveText, GroveSwapText, GroveIntro)} from '@autumnsgrove/lattice/ui';
import groveTermManifest from '$lib/data/grove-term-manifest.json';
| 组件 | 使用场景 | 行为 |
|---|---|---|
GroveTerm | 带弹出定义的交互式术语 | 启用时显示彩色下划线,点击后弹出说明。关闭时显示标准术语。 |
GroveSwap | 静默替换文本 | 响应式替换文本。无下划线,也无交互。 |
GroveText | 解析数据字符串中的 [[term]] 语法 | 将 [[bloom|posts]] 渲染为交互式 GroveTerm 组件。 |
GroveSwapText | 静默解析 [[term]] 语法 | 使用相同的解析方式,但静默替换(不弹出说明)。 |
GroveIntro | “我们称之为 X”页面横幅 | 在页面标题下方显示标准化介绍。 |
用法:
<GroveTerm term="bloom" manifest={groveTermManifest} />
<GroveTerm term="wanderer" manifest={groveTermManifest}>wanderers</GroveTerm>
<GroveSwap term="arbor" manifest={groveTermManifest} />
<GroveText
content="Your [[bloom|posts]] live in your [[garden|blog]]."
manifest={groveTermManifest}
/>
<GroveTerm term="meadow" />
<GroveTerm term="meadow" icon />
关键规则:
- 切勿在面向用户的 UI 中硬编码 Grove 术语。 始终使用 GroveTerm 组件。
- 新访客的默认设置为关闭。 优先显示标准、熟悉的术语。
- URL 始终使用 Grove 术语(
/porch、/garden),不受显示模式影响。 - 品牌术语(Grove)和订阅层级(Seedling/Sapling/Oak/Evergreen)始终按原样显示。
- 数据驱动内容(FAQ 条目、定价细则、功能列表)使用
[[term]]语法。 - Grove 模式存储:
@autumnsgrove/lattice/ui/stores中的groveModeStore。切换开关位于页脚。
完整的身份语言文档请参阅 docs/grove-user-identity.md,组件源代码请参阅 libs/engine/src/lib/ui/components/ui/groveterm/。
玻璃拟态模式
玻璃效果在展现背景装饰元素的同时,提升内容的可读性。
分层公式
Background (gradients, vines, nature)
↓
Decorative Elements (trees, clouds, particles)
↓
Glass Surface (translucent + blur)
↓
Content (text, cards, UI)
玻璃组件
import {(Glass, GlassCard, GlassButton, GlassOverlay)} from '@lattice/ui/ui';
<Glass variant="tint" class="p-6 rounded-xl">
<p>Readable text over busy backgrounds</p>
</Glass>
<GlassCard title="Settings" variant="default" hoverable>Content here</GlassCard>
<GlassButton variant="accent">Subscribe</GlassButton>
玻璃变体
| 变体 | 使用场景 | 浅色模式 | 深色模式 |
|---|---|---|---|
surface | 标题栏、导航栏 | 95% 白色 | 95% 石板色 |
tint | 背景上的文本 | 60% 白色 | 50% 石板色 |
card | 内容卡片 | 80% 白色 | 70% 石板色 |
accent | 提示、重点内容 | 30% 强调色 | 20% 强调色 |
overlay | 模态框背景遮罩 | 50% 黑色 | 60% 黑色 |
muted | 微妙的背景 | 40% 白色 | 30% 石板色 |
CSS 工具类
<div class="glass rounded-xl p-4">Basic glass</div>
<div class="glass-tint p-6">Text container</div>
<div class="glass-accent p-4">Highlighted section</div>
<nav class="glass-surface sticky top-0">Navbar</nav>
关键模式:粘性导航
<nav
class="sticky top-[73px] z-30 bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm border-b border-divider"
>
</nav>
季节主题系统
Grove 使用四季主题,每个季节都有独特的颜色、天气效果和氛围。
季节检测
import {season} from '$lib/stores/season'; const isSpring = $derived($season === 'spring'); const isAutumn
= $derived($season === 'autumn'); const isWinter = $derived($season === 'winter'); // Summer is the default
(no flag needed)
调色板系统
从 @autumnsgrove/lattice/ui/nature 或 $lib/components/nature/palette 导入
核心调色板(全年适用)
**强调色:**这些调色板中的十六进制颜色值用于自然主题 SVG、插画和品牌用途。对于使用强调色的 CSS 表面样式(按钮、链接、高亮),请改用
var(--grove-accent-*)令牌——用户的强调色可能不是绿色。请参阅强调色阶:var(--grove-accent)(纯色)、var(--grove-accent-dark)(悬停)、var(--grove-accent-N)(不透明度色调,级别为 5-80)。提交前钩子会强制执行此规则。
import { greens, bark, earth, natural } from "@autumnsgrove/lattice/ui/nature";
// Greens - organized dark-to-light for atmospheric depth
// NOTE: Use these for nature components and brand illustrations.
// For accent-colored UI surfaces, use var(--grove-accent-*) instead.
greens.darkForest; // #0d4a1c - Background trees
greens.deepGreen; // #166534 - Mid-distance
greens.grove; // #16a34a - Grove brand primary
greens.meadow; // #22c55e - Standard foliage
greens.spring; // #4ade80 - Bright accent
greens.mint; // #86efac - Light accent
greens.pale; // #bbf7d0 - Foreground highlights
// Bark - warm wood tones
bark.darkBark; // #3d2817 - Oak, older trees
bark.bark; // #5d4037 - Standard trunk
bark.warmBark; // #6B4423 - Pine, cedar
bark.lightBark; // #8b6914 - Young trees
// Earth - ground elements
(earth.soil, earth.mud, earth.clay, earth.sand, earth.stone, earth.pebble, earth.slate);
// Natural - cream and off-whites
(natural.cream, natural.aspenBark, natural.bone, natural.mushroom, natural.birchWhite);
春季调色板
import {
springFoliage,
springSky,
wildflowers,
cherryBlossoms,
cherryBlossomsPeak,
} from "@autumnsgrove/lattice/ui/nature";
// Spring Foliage - yellow-green new growth
springFoliage.sprout; // #65a30d - Distant new growth
springFoliage.newLeaf; // #84cc16 - Classic spring lime
springFoliage.freshGreen; // #a3e635 - Bright foreground
springFoliage.budding; // #bef264 - Pale new leaf
springFoliage.tender; // #d9f99d - Very pale
// Spring Sky
springSky.clear; // #7dd3fc - Clear morning
springSky.soft; // #bae6fd - Pale sky
// Wildflowers - unified meadow flower colors
wildflowers.buttercup; // #facc15 - Yellow
wildflowers.daffodil; // #fde047 - Pale yellow
wildflowers.crocus; // #a78bfa - Purple crocus
wildflowers.violet; // #8b5cf6 - Wild violets
wildflowers.purple; // #a855f7 - Lupine, thistle
wildflowers.lavender; // #c4b5fd - Distant masses
wildflowers.tulipPink; // #f9a8d4 - Pink tulips
wildflowers.tulipRed; // #fb7185 - Red tulips
wildflowers.white; // #fefefe - Daisies, trillium
// Cherry Blossoms - summer standard
cherryBlossoms.deep; // #db2777 - Dense centers
cherryBlossoms.standard; // #ec4899 - Standard blossom
cherryBlossoms.light; // #f472b6 - Light petals
cherryBlossoms.pale; // #f9a8d4 - Pale blossoms
cherryBlossoms.falling; // #fbcfe8 - Falling petals
// Cherry Blossoms Peak - vibrant spring (one shade brighter!)
cherryBlossomsPeak.deep; // #ec4899
cherryBlossomsPeak.standard; // #f472b6
cherryBlossomsPeak.light; // #f9a8d4
cherryBlossomsPeak.pale; // #fbcfe8
cherryBlossomsPeak.falling; // #fce7f3
统一花卉调色板(新增!)
flowers 命名空间将所有花卉颜色整合到一个结构清晰的结构中:
import { flowers } from "@autumnsgrove/lattice/ui/nature";
// Meadow wildflowers (yellows, purples, pinks, whites)
flowers.wildflower.buttercup; // #facc15 - Yellow
flowers.wildflower.daffodil; // #fde047 - Pale yellow
flowers.wildflower.crocus; // #a78bfa - Purple crocus
flowers.wildflower.violet; // #8b5cf6 - Wild violets
flowers.wildflower.purple; // #a855f7 - Lupine, thistle
flowers.wildflower.lavender; // #c4b5fd - Distant masses
flowers.wildflower.tulipPink; // #f9a8d4 - Pink tulips
flowers.wildflower.tulipRed; // #fb7185 - Red tulips
flowers.wildflower.white; // #fefefe - Daisies, trillium
// Cherry blossoms - standard summer
flowers.cherry.deep; // #db2777
flowers.cherry.standard; // #ec4899
flowers.cherry.light; // #f472b6
flowers.cherry.pale; // #f9a8d4
flowers.cherry.falling; // #fbcfe8
// Cherry blossoms at peak bloom - vibrant spring
flowers.cherryPeak.deep; // #ec4899
flowers.cherryPeak.standard; // #f472b6
flowers.cherryPeak.light; // #f9a8d4
flowers.cherryPeak.pale; // #fbcfe8
flowers.cherryPeak.falling; // #fce7f3
请使用 flowers.wildflower,而非 accents.flower——后者已弃用。
秋季和冬季调色板
import { autumn, autumnReds, winter } from "@autumnsgrove/lattice/ui/nature";
// Autumn - warm fall foliage (dark-to-light for depth)
autumn.rust; // #9a3412 - Deep background
autumn.ember; // #c2410c - Oak-like
autumn.pumpkin; // #ea580c - Maple mid-tones
autumn.amber; // #d97706 - Classic fall
autumn.gold; // #eab308 - Aspen/birch
autumn.honey; // #facc15 - Bright foreground
autumn.straw; // #fde047 - Pale dying leaves
// Autumn Reds - cherry/maple fall foliage
autumnReds.crimson; // #be123c - Deep maple
autumnReds.scarlet; // #e11d48 - Bright cherry
autumnReds.rose; // #f43f5e - Light autumn
autumnReds.coral; // #fb7185 - Pale accent
// Winter - frost, snow, ice + frosted evergreens
(winter.snow, winter.frost, winter.ice, winter.glacier);
(winter.frostedPine, winter.winterGreen, winter.coldSpruce);
(winter.winterSky, winter.twilight, winter.overcast);
(winter.bareBranch, winter.frostedBark, winter.coldWood);
(winter.hillDeep, winter.hillMid, winter.hillNear, winter.hillFront);
强调色调色板
import { accents, wildflowers } from "@autumnsgrove/lattice/ui/nature";
// Mushrooms - fairy tale pops of color
(accents.mushroom.redCap, accents.mushroom.orangeCap, accents.mushroom.brownCap);
(accents.mushroom.spots, accents.mushroom.gill);
// Firefly - bioluminescence
(accents.firefly.glow, accents.firefly.warmGlow, accents.firefly.body);
// Berry - rich saturated
(accents.berry.ripe, accents.berry.elderberry, accents.berry.red);
// Water - cool blue spectrum
(accents.water.surface, accents.water.deep, accents.water.shallow, accents.water.lily);
// Sky - time of day
(accents.sky.dayLight, accents.sky.dayMid, accents.sky.sunset, accents.sky.night, accents.sky.star);
// Birds - species-specific colors
(accents.bird.cardinalRed, accents.bird.cardinalMask, accents.bird.cardinalBeak);
(accents.bird.chickadeeCap, accents.bird.chickadeeBody, accents.bird.chickadeeBelly);
(accents.bird.robinBody, accents.bird.robinBreast, accents.bird.robinBeak);
(accents.bird.bluebirdBody, accents.bird.bluebirdWing, accents.bird.bluebirdBreast);
// NOTE: accents.flower is deprecated - use flowers.wildflower instead
季节辅助函数
import {
getSeasonalGreens,
getCherryColors,
isTreeBare,
pickRandom,
pickFrom,
} from "@autumnsgrove/lattice/ui/nature";
// Get foliage colors mapped to season
const foliage = getSeasonalGreens(season);
// spring → springFoliage colors
// summer → greens
// autumn → autumn palette
// winter → frosted evergreen colors
// Get cherry tree colors by season
const cherryColors = getCherryColors(season);
// spring → cherryBlossomsPeak (vibrant!)
// summer → cherryBlossoms (standard)
// autumn → autumnReds
// winter → null (bare tree)
// Check if deciduous tree is bare
if (isTreeBare("cherry", "winter")) {
/* no foliage */
}
// Random color selection for natural variation
const randomGreen = pickRandom(greens);
const specificGreen = pickFrom(greens, ["grove", "meadow"]);
已弃用的别名(仍可使用)
// These work but will be removed in v1.0:
import { spring, pinks, springBlossoms } from "@autumnsgrove/lattice/ui/nature";
// spring → use springFoliage, wildflowers, springSky instead
// pinks → use cherryBlossoms instead
// springBlossoms → use cherryBlossomsPeak instead
// accents.flower → use flowers.wildflower instead
季节氛围概览
| 季节 | 主要颜色 | 氛围 |
|---|---|---|
| 春季 | springFoliage、cherryBlossomsPeak、wildflowers | 新生、希望 |
| 夏季 | greens、cherryBlossoms | 生长、温暖 |
| 秋季 | autumn、autumnReds | 丰收、沉思 |
| 冬季 | winter(霜、雪、覆霜的松树) | 休憩、静谧 |
季节性天气效果
{#if isWinter}
<SnowfallLayer count={40} zIndex={5} opacity={{ min: 0.4, max: 0.8 }} spawnDelay={8} />
{/if}
{#if isSpring}
<FallingPetalsLayer count={80} zIndex={100} opacity={{ min: 0.5, max: 0.9 }} />
{/if}
{#if isAutumn}
<FallingLeavesLayer
trees={forestTrees}
season={$season}
minLeavesPerTree={2}
maxLeavesPerTree={4}
/>
{/if}
季节性背景渐变
<main class="min-h-screen transition-colors duration-1000
{isWinter ? 'bg-gradient-to-b from-slate-200 via-slate-100 to-slate-50 dark:from-slate-900 dark:via-slate-800 dark:to-slate-700' : ''}
{isAutumn ? 'bg-gradient-to-b from-orange-100 via-amber-50 to-yellow-50 dark:from-slate-900 dark:via-amber-950 dark:to-orange-950' : ''}
{isSpring ? 'bg-gradient-to-b from-pink-50 via-sky-50 to-lime-50 dark:from-slate-900 dark:via-pink-950 dark:to-lime-950' : ''}
{/* Summer default */} 'bg-gradient-to-b from-sky-100 via-sky-50 to-emerald-50 dark:from-slate-900 dark:via-slate-800 dark:to-emerald-950'
">
何时使用季节主题
- 路线图页面——通过季节隐喻展现进展
- 故事/关于页面——营造氛围并建立情感联系
- 交互式演示——让用户切换季节(如 /forest)
- 帮助文章——可考虑加入季节装饰,让长内容更易阅读
- 任何想增添魔法感的地方——根据页面用途自行判断
随机化森林
森林应充满生机,并在每次访问时呈现不同的面貌。
树木生成模式
interface GeneratedTree {
id: number;
x: number; // percentage from left (5-93% to avoid edges)
size: number; // base width in pixels
aspectRatio: number; // height = size * aspectRatio (1.0-1.5 range)
treeType: TreeType; // 'logo' | 'pine' | 'cherry' | 'aspen' | 'birch'
opacity: number; // 0.5-0.9 for depth
zIndex: number; // larger trees = higher z-index
}
// Aspect ratio creates natural height variation
const TREE_ASPECT_RATIO_RANGE = { min: 1.0, max: 1.5 };
function generateSectionTrees(count: number): GeneratedTree[] {
const trees: GeneratedTree[] = [];
const usedPositions: number[] = [];
for (let i = 0; i < count; i++) {
// Find non-overlapping position
let x: number;
let attempts = 0;
do {
x = 5 + Math.random() * 88;
attempts++;
} while (usedPositions.some((pos) => Math.abs(pos - x) < 8) && attempts < 20);
usedPositions.push(x);
const size = 80 + Math.random() * 80;
const aspectRatio = 1.0 + Math.random() * 0.5;
const opacity = 0.5 + Math.random() * 0.4;
const zIndex = size > 130 ? 3 : size > 100 ? 2 : 1;
trees.push({
id: i,
x,
size,
aspectRatio,
treeType: pickRandom(treeTypes),
opacity,
zIndex,
});
}
return trees.sort((a, b) => a.x - b.x);
}
重新生成时机
- 挂载时——页面加载时生成一次树木
- 调整大小时(显著变化)——仅当视口范围发生明显变化时
- 滚动时绝不重新生成——阅读期间保持森林稳定
渲染树木
{#each forestTrees as tree (tree.id)}
<div
class="absolute"
style="
left: {tree.x}%;
bottom: 0;
width: {tree.size}px;
height: {tree.size * tree.aspectRatio}px;
opacity: {tree.opacity};
z-index: {tree.zIndex};
transform: translateX(-50%);
"
>
{#if tree.treeType === "logo"}
<Logo class="w-full h-full" season={$season} animate />
{:else if tree.treeType === "pine"}
<TreePine class="w-full h-full" season={$season} animate />
{:else if tree.treeType === "cherry"}
<TreeCherry class="w-full h-full" season={$season} animate />
{:else if tree.treeType === "aspen"}
<TreeAspen class="w-full h-full" season={$season} animate />
{:else if tree.treeType === "birch"}
<TreeBirch class="w-full h-full" season={$season} animate />
{/if}
</div>
{/each}
响应式密度
function calculateDensity(): number {
const width = window.innerWidth;
if (width < 768) return 1; // Mobile: base count
if (width < 1024) return 1.3; // Tablet
if (width < 1440) return 1.8; // Desktop
if (width < 2560) return 2.5; // Large desktop
return 3.5; // Ultrawide
}
自然主题组件
Grove 提供了丰富的装饰组件库。可通过以下方式探索:
# Trees
ls landing/src/lib/components/trees/
ls landing/src/lib/components/nature/trees/
# Weather (seasonal particles)
ls landing/src/lib/components/nature/weather/
# Sky (clouds, stars, moon)
ls landing/src/lib/components/nature/sky/
# Botanical (leaves, petals, vines)
ls landing/src/lib/components/nature/botanical/
# Ground (flowers, grass, mushrooms)
ls landing/src/lib/components/nature/ground/
# Structural (lattice, lanterns, paths)
ls landing/src/lib/components/nature/structural/
# Birds (cardinals, robins, bluebirds)
ls landing/src/lib/components/nature/creatures/
核心组件
| 组件 | 用途 | 属性示例 |
|---|---|---|
Logo | Grove 树,随季节变化 | season、animate、breathing |
TreePine | 常青树,秋季依然翠绿 | season、animate |
TreeCherry | 春季开花,冬季枝叶凋零 | season、animate |
TreeAspen / TreeBirch | 落叶树,呈现季节色彩 | season、animate |
Cloud | 装饰性天空元素 | variant、animate、speed、direction |
SnowfallLayer | 冬季粒子效果 | count、opacity、spawnDelay |
FallingPetalsLayer | 春季樱花花瓣 | count、opacity、fallDuration |
FallingLeavesLayer | 秋季落叶(与树木关联) | trees、season |
Cardinal / Chickadee | 冬季鸟类 | facing |
Robin / Bluebird | 春季鸟类 | facing |
Vine | 装饰性常春藤/藤蔓 | 各不相同 |
Lantern | 温暖的发光点 | 各不相同 |
按季节分类的鸟类
{#if isWinter}
<Cardinal facing="right" style="..." />
<Chickadee facing="left" style="..." />
{/if}
{#if isSpring}
<Robin facing="right" style="..." />
<Bluebird facing="left" style="..." />
{/if}
午夜绽放配色
适用于梦幻、遥远未来、神秘风格的内容。那家坐落在明日边缘的茶馆。
import { midnightBloom } from "$lib/components/nature/palette";
// Available colors:
midnightBloom.deepPlum; // #581c87 - Night sky depth
midnightBloom.purple; // #7c3aed - Soft purple glow
midnightBloom.violet; // #8b5cf6 - Lighter accent
midnightBloom.amber; // #f59e0b - Lantern warmth
midnightBloom.warmCream; // #fef3c7 - Tea steam, page glow
midnightBloom.softGold; // #fcd34d - Fairy lights
午夜绽放样式
<section class="bg-gradient-to-b from-orange-950/50 via-purple-950 to-slate-950">
<StarCluster count={12} class="absolute top-12 left-[10%]" />
<Moon phase="waning-crescent" class="absolute top-20 right-[15%] w-16 h-16 opacity-60" />
<Firefly count={8} class="absolute inset-0" />
<blockquote class="bg-purple-900/30 backdrop-blur-sm border border-purple-700/30 rounded-lg p-6">
<p class="text-purple-200 italic">Dreamy quote here...</p>
</blockquote>
</section>
图标:仅使用 Lucide
绝不使用表情符号。始终使用 Lucide 图标。
import {(MapPin, Check, Leaf, Trees, Mail)} from '@lucide/svelte';
<MapPin class="w-4 h-4" />
<Check class="w-5 h-5 text-green-500" />
标准化图标映射
在整个项目中统一使用以下图标:
| 概念 | 图标 | 说明 |
|---|---|---|
| 导航 | ||
| 首页 | Home | |
| 关于 | Info | |
| 愿景 | Telescope | 展望未来 |
| 路线图 | Map | 旅程/方向 |
| 定价 | HandCoins | 金钱/货币 |
| 知识 | BookOpen | 学习/文档 |
| 森林 | Trees | 社区博客 |
| 博客 | PenLine | 写作 |
| 功能 | ||
| 电子邮件 | Mail | |
| 存储 | HardDrive | |
| 主题 | Palette | 自定义 |
| 身份验证 | ShieldCheck | 安全 |
| 云 | Cloud | 远程/无服务器 |
| 搜索 | SearchCode | 代码/高级搜索 |
| 归档 | Archive | 备份 |
| 上传 | Upload | |
| 视频 | Video | |
| 评论 | MessagesSquare | 用户讨论 |
| GitHub | Github | 指向 GitHub 的外部链接 |
| 状态 | ||
| 成功 | Check | 已完成/有效 |
| 错误 | X | 失败/关闭 |
| 加载中 | Loader2 | 使用 animate-spin |
| 内容 | ||
| 帖子 | FileText | 博客帖子 |
| 标签 | Tag | 分类 |
| 成长 | Sprout | Grove 品牌,新起点 |
| 心形 | Heart | 爱、关怀 |
| 外部 | ExternalLink | 在新标签页中打开 |
| 位置 | MapPin | 当前位置 |
| 阶段 | ||
| 即将推出 | Seedling | 正在生长的事物 |
| 精炼 | Gem | 润色、品质 |
| 梦想 | Sparkles | 神秘(谨慎使用!) |
| 夜晚 | Star | 午夜主题 |
| 操作 | ||
| 入门 | Compass | 指引 |
| 最新动态 | Megaphone | 公告 |
| 后续步骤 | Lightbulb | 想法 |
文件中的图标映射表
在每个使用图标的组件/页面顶部创建一致的图标映射表:
// landing/src/lib/utils/icons.ts - Centralized icon registry
import {
Mail,
HardDrive,
Palette,
ShieldCheck,
Cloud,
SearchCode,
Archive,
Upload,
MessagesSquare,
Github,
Check,
X,
Loader2,
FileText,
Tag,
Sprout,
Heart,
ExternalLink,
MapPin, // ... etc
} from "@lucide/svelte";
export const featureIcons = {
mail: Mail,
harddrive: HardDrive,
palette: Palette,
shieldcheck: ShieldCheck,
cloud: Cloud,
searchcode: SearchCode,
// ... all mapped icons
} as const;
export const stateIcons = {
success: Check,
error: X,
loading: Loader2,
} as const;
然后在组件中使用:
<script lang="ts">
import { featureIcons } from "$lib/utils/icons";
</script>
{#each features as feature}
<svelte:component this={featureIcons[feature.icon]} class="w-5 h-5" />
{/each}
优点:
- 为所有图标提供唯一可信来源
- 避免出现未定义图标错误
- 易于维护和扩展
- 可在整个项目中重复使用
图标使用指南
- 始终使用图标映射表——不要在每个组件中硬编码图标导入
- 避免过度使用 Sparkles——仅在真正神秘/魔幻的场景中使用
- 保持一致——同一概念在任何地方都使用相同图标
- 语义明确——选择能够传达含义而非仅作装饰的图标
- 从中央工具模块导出——所有图标集都使用
landing/src/lib/utils/icons.ts
图标尺寸
<span class="inline-flex items-center gap-1.5">
<Leaf class="w-4 h-4" /> Feature name
</span>
<button class="p-2">
<Menu class="w-5 h-5" />
</button>
<Gem class="w-8 h-8 text-amber-400" />
图标组合(构建块模式)
理念:“无需绘制树林,只需将它们排列起来。”
要创建自定义徽标、插图或装饰元素,可组合现有的 Lucide 图标,而不是从头绘制自定义 SVG。这样可以确保视觉风格与图标系统保持一致。
为什么采用这种模式?
- 保持一致 — 图标符合 Lucide 的美学风格(24x24 网格、2px 描边、圆形端点)
- 尽量减少自定义代码 — 让 Lucide 承担繁重的工作
- 易于维护 — 更新 Lucide 时,组合也会随之更新
- 采用 MIT 许可证 — 所有路径均来自开源图标
如何提取 Lucide 路径
Lucide 图标使用 24×24 的 viewBox 和 2px 描边。直接从源代码中提取路径:
# Find icon paths in Lucide source
curl -s https://raw.githubusercontent.com/lucide-icons/lucide/main/icons/tree-pine.svg
# Look for the <path d="..." /> elements
适用于 Grove 组合的关键 Lucide 图标路径:
// TreePine - conifer silhouette
const treePine = {
canopy:
"m17 14 3 3.3a1 1 0 0 1-.7 1.7H4.7a1 1 0 0 1-.7-1.7L7 14h-.3a1 1 0 0 1-.7-1.7L9 9h-.2A1 1 0 0 1 8 7.3L12 3l4 4.3a1 1 0 0 1-.8 1.7H15l3 3.3a1 1 0 0 1-.7 1.7H17Z",
trunk: "M12 22v-3",
};
// TreeDeciduous - deciduous/round tree
const treeDeciduous = {
canopy:
"M8 19a4 4 0 0 1-2.24-7.32A3.5 3.5 0 0 1 9 6.03V6a3 3 0 1 1 6 0v.04a3.5 3.5 0 0 1 3.24 5.65A4 4 0 0 1 16 19Z",
trunk: "M12 19v3",
};
// Moon - crescent moon
const moon =
"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401";
// Flame - campfire/hearth
const flame =
"M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4";
使用 SVG 变换进行组合
使用 <g transform="..."> 来定位、缩放和旋转图标:
<svg viewBox="0 0 48 32" fill="none">
<g
transform="translate(2, 4) scale(0.85)"
stroke={color}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d={treePine.canopy} />
<path d={treePine.trunk} />
</g>
<g
transform="translate(20, 8) scale(0.65) rotate(-5, 12, 12)"
stroke={color}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
opacity="0.7"
>
<path d={treePine.canopy} />
<path d={treePine.trunk} />
</g>
<circle cx="30" cy="10" r="1.5" fill={glowColor} opacity="0.8" />
<path d="M2 28h44" stroke={color} stroke-width="1.5" opacity="0.3" />
</svg>
变换速查表
| 变换 | 效果 | 示例 |
|---|---|---|
translate(x, y) | 移动原点 | translate(20, 8) 将图标向右移动 20,向下移动 8 |
scale(s) | 等比缩放 | scale(0.65) 将图标缩小至原尺寸的 65% |
rotate(deg, cx, cy) | 绕指定点旋转 | rotate(-5, 12, 12) 绕中心旋转 5° |
| 组合 | 链式应用变换 | translate(20, 8) scale(0.65) rotate(-5, 12, 12) |
示例:Grove 徽标组合
查看 /landing/src/lib/components/logo-concepts/ 中的实际实现:
| 徽标 | 组合 |
|---|---|
LogoFireflyForest | TreePine + TreeDeciduous + 发光圆圈 |
LogoGatheringHearth | 两棵朝向中心 Flame 倾斜的树 |
LogoStarlightPines | 两棵 TreePine + Moon + 星形圆圈 |
LogoShelter | 两棵 TreePine 组成拱门 + Moon |
LogoWinterGrove | TreePine + 雪地线条装饰 |
指南
- 以 Lucide 路径作为主要结构 — 树、月亮、火焰等
- 自定义 SVG 仅用于简单图形 — 圆形(萤火虫)、线条(地面、雪地)
- 保持 Lucide 的样式 — 2px 描边、圆形端点和连接处、统一的不透明度
- 用不透明度和缩放营造层次感 — 较大 = 前景(不透明度 0.9),较小 = 背景(0.5-0.7)
- 保持合理的 viewBox 宽高比 — 水平组合使用 40×32 或 48×32
适用场景
- 徽标与品牌形象 — 将图标组合成独特的标志
- 插图 — 构建场景(森林、天空等)
- 自定义图标 — Lucide 中没有完全符合需求的图标时
- 季节变化 — 沿用同一组合,替换不同元素(雪、花朵)
移动端注意事项
溢出菜单模式
无法放入桌面导航的项目应放入移动端抽屉菜单:
<button onclick={() => (mobileMenuOpen = true)} class="md:hidden p-2">
<Menu class="w-5 h-5" />
</button>
<MobileMenu bind:open={mobileMenuOpen} onClose={() => (mobileMenuOpen = false)} />
移动端装饰元素
| 元素 | 移动端处理方式 |
|---|---|
| 树木 | 减少数量并简化(密度乘数 = 1) |
| 粒子 | 减少数量(雪花从 40 减至 20) |
| 云朵 | 隐藏部分,保留 2-3 朵 |
| 复杂动画 | 减少或禁用 |
| 触控目标 | 至少为 44x44px |
性能指南
<SnowfallLayer count={isLargeScreen ? 100 : 40} ... />
{#if !prefersReducedMotion}
<FallingLeavesLayer ... />
{/if}
适用场景
| 样式 | 适用场景 |
|---|---|
| 玻璃拟态 | 背景上的文字、导航栏、卡片、模态框 |
| 随机森林 | 故事页面、关于页面、视觉展示区 |
| 季节主题 | 路线图、时间线、情感叙事 |
| 午夜绽放 | 未来功能、梦想、神秘内容 |
| 天气粒子 | Hero 区域、季节间的过渡 |
| 鸟类 | 为森林场景增添生机、作为季节指示 |
不适用场景
| 样式 | 避免使用的场景 |
|---|---|
| 繁复装饰 | 数据密集型页面、管理界面、表单 |
| 粒子效果 | 对性能要求极高的页面、存在无障碍方面的顾虑 |
| 季节性色彩 | 需要保持色彩一致性的品牌关键场景 |
| 多层玻璃效果 | 可能导致模糊效果影响性能 |
| 随机化 | 内容需要在不同会话间保持一致 |
| 复杂森林场景 | 移动优先页面、简单的信息展示内容 |
参考页面
参考以下页面,了解实现模式:
/forest— 包含所有季节的完整随机森林/roadmap— 季节性分区、渐进式装饰、午夜绽放/vision— 带玻璃拟态提示框的叙事页面
OG 图片(社交媒体预览)
Grove 使用动态 OG 图片作为社交媒体预览(Discord、Twitter、iMessage 等)。
架构
由于 SvelteKit + Cloudflare Pages 在打包 WASM 方面存在限制,OG 图片由 og.grove.place 上的独立 Cloudflare Worker 生成。
grove.place/api/og?title=X
↓ 302 redirect
og.grove.place/?title=X
↓ workers-og
PNG image (1200×630)
API
GET https://og.grove.place/?title=X&subtitle=Y&accent=HEX
| 参数 | 默认值 | 说明 |
|---|---|---|
title | "Grove" | 主标题(最多 100 个字符) |
subtitle | "A place to Be." | 副标题(最多 200 个字符) |
accent | "16a34a" | 不带 # 的十六进制颜色(森林绿) |
为新页面添加 OG
使用 SEO 组件处理 OG 元标签:
<script>
import SEO from "$lib/components/SEO.svelte";
</script>
<SEO
title="Page Title"
description="Page description for search engines"
ogImage="/api/og?title=Page%20Title&subtitle=Custom%20subtitle"
/>
文件
services/og-worker/— 独立 Worker(使用workers-og)landing/src/routes/api/og/+server.ts— 指向 og.grove.place 的代理landing/src/lib/components/SEO.svelte— 元标签管理
与其他技能集成
为 Grove UI 编写文本(工具提示、按钮、引导流程、错误消息)时,请先调用 grove-documentation 技能。文案风格应与视觉效果相匹配。
典型流程:
- 设计 UI 组件/页面
- 对任何面向用户的文本启用
grove-documentation - 遵循 Grove 的语气原则编写内容
- 返回视觉实现
使用 Showroom 审核组件
在验证完整页面之前,先审核单个组件。 Showroom 会在隔离环境中渲染单个 .svelte 组件,运行设计规范检查(颜色令牌、间距网格、排版、焦点样式、标题层级),并与视觉基线进行差异比较。它能捕获完整页面 Glimpse 检查遗漏的问题。
# Audit a component in isolation
uv run --project tools/glimpse glimpse showroom \
libs/engine/src/lib/ui/components/primitives/button/button.svelte
# Scaffold a fixture for a new component (generates .showroom.ts with scenarios)
uv run --project tools/glimpse glimpse showroom \
libs/engine/src/lib/ui/components/ui/MyComponent.svelte --scaffold
# Audit with specific scenario and theme
uv run --project tools/glimpse glimpse showroom \
libs/engine/src/lib/ui/components/ui/MyComponent.svelte \
--scenario error --theme dark
# Update visual baselines after intentional design changes
uv run --project tools/glimpse glimpse showroom \
libs/engine/src/lib/ui/components/ui/MyComponent.svelte --update-baselines
组件关卡: 每个新建或修改的组件都必须通过 glimpse showroom,之后才能进行页面级 Glimpse 验证。请先修复所有规范违规。
使用 Glimpse 进行视觉验证
发布任何 Grove 页面之前,都要亲自查看。 CI 通过不代表页面看起来正确。使用 Glimpse 捕获渲染后的页面并自行检查:
# Capture the page with Grove theme injection
uv run --project tools/glimpse glimpse capture http://localhost:5173/[page] \
--season autumn --theme dark --logs
# Verify all season × theme combos
uv run --project tools/glimpse glimpse matrix http://localhost:5173/[page]
# Interactive browse — click around, verify navigation and flows
uv run --project tools/glimpse glimpse browse http://localhost:5173/[page] \
--do "click links, scroll down, interact with elements" --screenshot-each --logs
迭代流程: 捕获 → 检查 → 修复 → 再次捕获。重复此流程,直到页面符合 Grove 的美学风格。不要发布自己没有检查过的内容。
快速检查清单
发布 Grove 页面前:
- Showroom:每个新建或修改的组件都通过
glimpse showroom检查,且无合规性违规 - Glimpse:已捕获页面并完成视觉检查
- Glimpse:所有目标季节的渲染均正确(使用
matrix) - Glimpse:
--logs输出中没有控制台错误 - 在繁杂背景上使用玻璃效果来提升文本可读性了吗?
- 使用 Lucide 图标,没有使用表情符号吗?
- 导航项是否有适用于移动端的溢出菜单?
- 装饰元素是否遵循
prefers-reduced-motion? - 触控目标尺寸是否至少为 44x44px?
- 季节配色是否符合页面的情感基调?
- 树木是否随机排列,并保持适当间距(至少留出 8% 的间隔)?
- 是否支持深色模式,并使用了合适的玻璃效果变体?
- 面向用户的文本是否符合 Grove 的语气(参见
grove-documentation)? - Grove 术语是否使用 GroveTerm 组件(而非硬编码)?
- 数据驱动的内容是否使用
[[term]]语法和 GroveText?