grove-ui-design

Create warm, nature-themed UI for Grove with glassmorphism, seasonal decorations, randomized forests, and accessible design patterns. Use when building pages, enhancing UI, or adding decorative elements.

$ 安裝

git clone https://github.com/majiayu000/claude-skill-registry /tmp/claude-skill-registry && cp -r /tmp/claude-skill-registry/skills/design/grove-ui-design ~/.claude/skills/claude-skill-registry

// tip: Run this command in your terminal to install the skill


name: grove-ui-design description: Create warm, nature-themed UI for Grove with glassmorphism, seasonal decorations, randomized forests, and accessible design patterns. Use when building pages, enhancing UI, or adding decorative elements.

Grove UI Design Skill

When to Activate

Activate this skill when:

  • Creating or enhancing pages for Grove sites
  • Adding decorative nature elements (trees, clouds, weather effects)
  • Implementing glassmorphism effects for readability
  • Working with the seasonal theme system
  • Building navigation patterns (navbar, mobile overflow menus)
  • Creating "story" pages that guide users through content
  • Ensuring mobile-friendly, accessible UI
  • Choosing icons or visual elements

The Grove Aesthetic

Grove is a place. It's nature-themed, warm, and inviting—like a midnight tea shop with good documentation.

Core Principles

Warm, introspective, queer, unapologetically building something meaningful.
Write with the warmth of a midnight tea shop and the clarity of good documentation.

Every design choice should feel:

  • Welcoming — like entering a cozy space
  • Organic — natural, not rigid or corporate
  • Readable — content-first, decorations enhance, never obstruct
  • Alive — subtle animations, seasonal changes, randomization

Glassmorphism Pattern

Glass effects create readability while revealing hints of background decoration.

The Layering Formula

Background (gradients, vines, nature)
    ↓
Decorative Elements (trees, clouds, particles)
    ↓
Glass Surface (translucent + blur)
    ↓
Content (text, cards, UI)

Glass Components

import { Glass, GlassCard, GlassButton, GlassOverlay } from '@groveengine/ui/ui';

<!-- Container with glass effect -->
<Glass variant="tint" class="p-6 rounded-xl">
  <p>Readable text over busy backgrounds</p>
</Glass>

<!-- Card with glass styling -->
<GlassCard title="Settings" variant="default" hoverable>
  Content here
</GlassCard>

<!-- Glass button -->
<GlassButton variant="accent">Subscribe</GlassButton>

Glass Variants

VariantUse CaseLight ModeDark Mode
surfaceHeaders, navbars95% white95% slate
tintText over backgrounds60% white50% slate
cardContent cards80% white70% slate
accentCallouts, highlights30% accent20% accent
overlayModal backdrops50% black60% black
mutedSubtle backgrounds40% white30% slate

CSS Utility Classes

<!-- Apply directly to any element -->
<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>

Key Pattern: Sticky Navigation

<nav class="sticky top-[73px] z-30 bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm border-b border-divider">
  <!-- Navigation content -->
</nav>

Seasonal Theme System

Grove uses four seasons, each with distinct colors, weather effects, and moods.

Season Detection

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)

Color Palette System

Import from: @autumnsgrove/groveengine/ui/nature or $lib/components/nature/palette

Core Palettes (Year-Round)

import { greens, bark, earth, natural } from '@autumnsgrove/groveengine/ui/nature';

// Greens - organized dark-to-light for atmospheric depth
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

Spring Palettes

import { springFoliage, springSky, wildflowers, cherryBlossoms, cherryBlossomsPeak } from '@autumnsgrove/groveengine/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

Autumn & Winter Palettes

import { autumn, autumnReds, winter } from '@autumnsgrove/groveengine/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

Accent Palettes

import { accents, wildflowers } from '@autumnsgrove/groveengine/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 wildflowers instead

Seasonal Helper Functions

import { getSeasonalGreens, getCherryColors, isTreeBare, pickRandom, pickFrom } from '@autumnsgrove/groveengine/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']);

Deprecated Aliases (Still Work)

// These work but will be removed in v1.0:
import { spring, pinks, springBlossoms } from '@autumnsgrove/groveengine/ui/nature';

// spring → use springFoliage, wildflowers, springSky instead
// pinks → use cherryBlossoms instead
// springBlossoms → use cherryBlossomsPeak instead
// accents.flower → use wildflowers instead

Season Mood Summary

SeasonPrimary ColorsMood
SpringspringFoliage, cherryBlossomsPeak, wildflowersRenewal, hope
Summergreens, cherryBlossomsGrowth, warmth
Autumnautumn, autumnRedsHarvest, reflection
Winterwinter (frost, snow, frosted pines)Rest, stillness

Seasonal Weather Effects

<!-- Winter: Snowfall -->
{#if isWinter}
  <SnowfallLayer count={40} zIndex={5} opacity={{ min: 0.4, max: 0.8 }} spawnDelay={8} />
{/if}

<!-- Spring: Cherry blossom petals -->
{#if isSpring}
  <FallingPetalsLayer count={80} zIndex={100} opacity={{ min: 0.5, max: 0.9 }} />
{/if}

<!-- Autumn: Falling leaves (tied to trees) -->
{#if isAutumn}
  <FallingLeavesLayer trees={forestTrees} season={$season} minLeavesPerTree={2} maxLeavesPerTree={4} />
{/if}

Seasonal Background Gradients

<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'
">

When to Use Seasons

  • Roadmap pages — Show progress through seasonal metaphor
  • Story/about pages — Create atmosphere and emotional connection
  • Interactive demos — Let users toggle seasons (like /forest)
  • Help articles — Consider seasonal decor to break up long content
  • Anywhere you want magic — Use judgment based on page purpose

Randomized Forests

The forest should feel alive and different every visit.

Tree Generation Pattern

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);
}

Regeneration Timing

  • On mount — Trees generate once when page loads
  • On resize (significant) — Only if viewport bracket changes dramatically
  • Never on scroll — Keep forest stable during reading

Rendering Trees

{#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}

Responsive Density

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
}

Nature Components

Grove has an extensive library of decorative components. Explore with:

# 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/

Key Components

ComponentUseExample Props
LogoGrove tree, seasonalseason, animate, breathing
TreePineEvergreen, stays green in autumnseason, animate
TreeCherryBlossoms in spring, bare in winterseason, animate
TreeAspen / TreeBirchDeciduous, seasonal colorsseason, animate
CloudDecorative sky elementvariant, animate, speed, direction
SnowfallLayerWinter particlescount, opacity, spawnDelay
FallingPetalsLayerSpring cherry blossomscount, opacity, fallDuration
FallingLeavesLayerAutumn leaves (tied to trees)trees, season
Cardinal / ChickadeeWinter birdsfacing
Robin / BluebirdSpring birdsfacing
VineDecorative ivy/vinesvaries
LanternWarm glow pointsvaries

Birds by Season

<!-- Winter birds -->
{#if isWinter}
  <Cardinal facing="right" style="..." />
  <Chickadee facing="left" style="..." />
{/if}

<!-- Spring birds -->
{#if isSpring}
  <Robin facing="right" style="..." />
  <Bluebird facing="left" style="..." />
{/if}

Midnight Bloom Palette

For dreamy, far-future, mystical content. The tea shop that exists at the edge of tomorrow.

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

Midnight Bloom Styling

<section class="bg-gradient-to-b from-orange-950/50 via-purple-950 to-slate-950">
  <!-- Stars -->
  <StarCluster count={12} class="absolute top-12 left-[10%]" />

  <!-- Moon -->
  <Moon phase="waning-crescent" class="absolute top-20 right-[15%] w-16 h-16 opacity-60" />

  <!-- Fireflies -->
  <Firefly count={8} class="absolute inset-0" />

  <!-- Content with purple glass -->
  <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>

Icons: Lucide Only

NEVER use emojis. ALWAYS use Lucide icons.

import { MapPin, Check, Leaf, Trees, Mail } from 'lucide-svelte';

<!-- Good -->
<MapPin class="w-4 h-4" />
<Check class="w-5 h-5 text-green-500" />

<!-- Bad - NEVER do this -->
<!-- ❌ 🌱 📧 ✅ -->

Standardized Icon Mapping

Use these icons consistently across the project:

ConceptIconNotes
Navigation
HomeHome
AboutInfo
VisionTelescopeLooking forward
RoadmapMapJourney/direction
PricingCircleDollarSignMoney/currency
KnowledgeBookOpenLearning/docs
ForestTreesCommunity blogs
BlogPenLineWriting
Features
EmailMail
StorageHardDrive
ThemingPaletteCustomization
AuthenticationShieldCheckSecurity
CloudCloudRemote/serverless
SearchSearch
ArchivesArchiveBackups
UploadUpload
VideoVideo
GitHubGithubExternal links to GitHub
States
SuccessCheckCompleted/valid
ErrorXFailed/close
LoadingLoader2With animate-spin
Content
PostsFileTextBlog posts
TagsTagCategorization
GrowthSproutGrove brand, new beginnings
HeartHeartLove, care
ExternalExternalLinkOpens new tab
LocationMapPinCurrent position
Phases
Coming SoonSeedlingSomething growing
RefinementGemPolish, quality
The DreamSparklesMystical (use sparingly!)
NightStarMidnight themes
Actions
Getting StartedCompassGuidance
What's NewMegaphoneAnnouncements
Next StepsLightbulbIdeas

Icon Usage Guidelines

  1. Avoid overusing Sparkles - Reserve for truly mystical/magical contexts
  2. Be consistent - Use the same icon for the same concept everywhere
  3. Semantic meaning - Choose icons that convey meaning, not just decoration

Icon Sizing

<!-- Inline with text -->
<span class="inline-flex items-center gap-1.5">
  <Leaf class="w-4 h-4" /> Feature name
</span>

<!-- Button icon -->
<button class="p-2">
  <Menu class="w-5 h-5" />
</button>

<!-- Large decorative -->
<Gem class="w-8 h-8 text-amber-400" />

Mobile Considerations

Overflow Menu Pattern

Desktop navigation items that don't fit should go to a mobile sheet menu:

<!-- Mobile menu button (visible md:hidden) -->
<button onclick={() => mobileMenuOpen = true} class="md:hidden p-2">
  <Menu class="w-5 h-5" />
</button>

<!-- Sheet menu -->
<MobileMenu bind:open={mobileMenuOpen} onClose={() => mobileMenuOpen = false} />

Decorative Elements on Mobile

ElementMobile Treatment
TreesReduce count, simplify (density multiplier = 1)
ParticlesReduce count (40→20 snowflakes)
CloudsHide some, keep 2-3
Complex animationsReduce or disable
Touch targetsMinimum 44x44px

Performance Guidelines

<!-- Reduce particle counts on mobile -->
<SnowfallLayer count={isLargeScreen ? 100 : 40} ... />

<!-- Skip complex effects for reduced-motion -->
{#if !prefersReducedMotion}
  <FallingLeavesLayer ... />
{/if}

When to Use

PatternGood For
GlassmorphismText over backgrounds, navbars, cards, modals
Randomized forestsStory pages, about pages, visual sections
Seasonal themesRoadmaps, timelines, emotional storytelling
Midnight BloomFuture features, dreams, mystical content
Weather particlesHero sections, transitions between seasons
BirdsAdding life to forest scenes, seasonal indicators

When NOT to Use

PatternAvoid When
Heavy decorationData-dense pages, admin interfaces, forms
Particle effectsPerformance-critical pages, accessibility concerns
Seasonal colorsBrand-critical contexts needing consistent colors
Multiple glass layersCan cause blur performance issues
RandomizationContent that needs to match between sessions
Complex forestsMobile-first pages, simple informational content

Reference Pages

Study these for implementation patterns:

  • /forest — Full randomized forest with all seasons
  • /roadmap — Seasonal sections, progressive decoration, midnight bloom
  • /vision — Narrative page with glass callouts

OG Images (Social Previews)

Grove uses dynamic OG images for social media previews (Discord, Twitter, iMessage, etc.).

Architecture

OG images are generated by a separate Cloudflare Worker at og.grove.place due to WASM bundling limitations with SvelteKit + Cloudflare Pages.

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
ParamDefaultDescription
title"Grove"Main title (max 100 chars)
subtitle"A place to Be."Subtitle (max 200 chars)
accent"16a34a"Hex color without # (forest green)

Adding OG to New Pages

Use the SEO component which handles OG meta tags:

<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"
/>

Files

  • packages/og-worker/ — Standalone Worker (uses workers-og)
  • landing/src/routes/api/og/+server.ts — Proxy to og.grove.place
  • landing/src/lib/components/SEO.svelte — Meta tag management

Quick Checklist

Before shipping a Grove page:

  • Glass effects used for text readability over busy backgrounds?
  • Lucide icons, no emojis?
  • Mobile overflow menu for navigation items?
  • Decorative elements respect prefers-reduced-motion?
  • Touch targets at least 44x44px?
  • Seasonal colors match the page's emotional tone?
  • Trees randomized with proper spacing (8% minimum gap)?
  • Dark mode supported with appropriate glass variants?