# CLAUDE.md - Vibery Kits Website

## Project Overview

Astro-based website for browsing and installing Claude Code templates via `npx vibery install <template>`. Currently being migrated to **Astro + Vue Islands** for better component reactivity and easier theming.

## Template Maintenance CLI

Use CLI instead of editing JSON directly (saves tokens):

```bash
npm run tpl:report     # Health check
npm run tpl:validate   # Cross-reference check
npm run tpl -- add agent name "desc"
npm run tpl -- remove agent name
npm run tpl -- search "query"
```

Full docs: `.claude/skills/template-ops/SKILL.md`

## Current State (Dec 2024)

### Completed

- **Warm Terminal** design system implemented in Tailwind
- Core Astro components updated with warm-\* classes
- Stacks JSON data created (12 curated stacks with credits)
- Outcomes JSON data created (8 outcome flows)
- StacksGrid.astro component created
- Hero.astro updated with wizard section

### Pending Build Fixes

Some components still have old `neon-*` class references:

- `ClientScripts.astro` - has neon classes in JS strings
- `TemplatesGrid.astro` - minor neon class references

### Data Files

- `src/data/stacks.json` - 12 curated stacks (Next.js, FastAPI, Security, DevOps, etc.)
- `src/data/outcomes.json` - 8 outcome flows for wizard
- `src/data/templates.json` - Main template catalog

## Design System: Warm Terminal

### Color Palette (tailwind.config.mjs)

```
warm-bg-deep: #0c0a09      (Stone-950, main background)
warm-bg-surface: #1c1917   (Stone-900, cards)
warm-bg-elevated: #292524  (Stone-800, hover)
warm-bg-hover: #44403c     (Stone-700, active)

warm-text-primary: #fafaf9
warm-text-secondary: #a8a29e
warm-text-tertiary: #78716c
warm-text-muted: #57534e

warm-accent: #e07256       (Terracotta - Claude-inspired)
warm-accent-hover: #c85a42
```

### Fonts

- Sans: Satoshi (primary)
- Mono: IBM Plex Mono (code)

## Recommended Migration: Astro + Vue Islands

### Why Vue Islands (not full Vue SPA)

1. Static content doesn't need JS (SEO-friendly)
2. Only Cart, Modal, Filters need reactivity
3. Keeps Cloudflare deployment unchanged
4. Incremental migration is safer

### Target Folder Structure

```
src/
├── components/
│   ├── astro/              # Static components
│   │   ├── Header.astro
│   │   ├── Hero.astro
│   │   ├── StacksGrid.astro
│   │   └── TemplateCard.astro
│   └── vue/                # Interactive components
│       ├── SearchBar.vue
│       ├── FilterBar.vue
│       ├── CartSidebar.vue
│       ├── TemplateModal.vue
│       └── WizardSection.vue
├── composables/            # Vue 3 composables
│   ├── useCart.ts
│   ├── useModal.ts
│   └── useSearch.ts
├── types/
│   ├── template.ts
│   └── cart.ts
└── data/
    ├── templates.json
    ├── stacks.json
    └── outcomes.json
```

### CSS Variables for Easy Theming

Add to global.css for theme switching:

```css
:root {
  --color-accent: #e07256;
  --color-accent-hover: #c85a42;
  --color-bg-deep: #0c0a09;
  --color-bg-surface: #1c1917;
  /* ... */
}

[data-theme="ocean"] {
  --color-accent: #38bdf8;
  --color-bg-deep: #0f172a;
}
```

## Development Commands

```bash
# Install dependencies
npm install

# Development server
npm run dev

# Build for production
npm run build

# Preview build
npm run preview

# Add Vue integration (for migration)
npx astro add vue
```

## Component Migration Priority

### High Priority (Interactive)

1. **Cart** → `CartSidebar.vue` - State management, persistence
2. **Modal** → `TemplateModal.vue` - Dynamic content, actions
3. **FilterBar** → `FilterBar.vue` - Filter state, navigation
4. **Search** → `SearchBar.vue` - Live filtering

### Medium Priority (Mostly Static)

5. **Hero wizard** → `WizardSection.vue` - Outcome selection
6. **StacksGrid** → Keep Astro, add Vue install buttons

### Low Priority (Static)

7. Header, TemplateCard - Keep as Astro

## Key Files

| File                                 | Purpose                                |
| ------------------------------------ | -------------------------------------- |
| `tailwind.config.mjs`                | Warm Terminal theme config             |
| `src/styles/global.css`              | Base styles, component classes         |
| `src/components/ClientScripts.astro` | All client JS (migrate to composables) |
| `src/data/stacks.json`               | 12 curated stacks                      |
| `src/data/outcomes.json`             | 8 outcome wizard flows                 |
| `astro.config.mjs`                   | Astro + Cloudflare config              |

## Sample Vue Component Pattern

```vue
<script setup lang="ts">
import { computed } from "vue";
import { useCart } from "@/composables/useCart";

const props = defineProps<{
  name: string;
  type: string;
}>();

const { addItem, hasItem } = useCart();
const isInCart = computed(() => hasItem(props.name));
</script>

<template>
  <article
    class="bg-surface-card border border-border-subtle rounded-warm p-5
                  hover:border-brand-primary transition-all"
  >
    <h3 class="text-content-primary">{{ name }}</h3>
    <button
      class="btn-primary"
      :class="{ 'bg-success': isInCart }"
      @click="addItem({ name, type })"
    >
      {{ isInCart ? "Added" : "Add" }}
    </button>
  </article>
</template>
```

## Sample Composable Pattern

```typescript
// composables/useCart.ts
import { ref, computed, watch } from "vue";

const items = ref<CartItem[]>([]);

export function useCart() {
  const count = computed(() => items.value.length);

  function addItem(item: CartItem) {
    if (!hasItem(item.name)) items.value.push(item);
  }

  function hasItem(name: string) {
    return items.value.some((i) => i.name === name);
  }

  return { items, count, addItem, hasItem };
}
```

## Migration Checklist

### Phase 1: Setup

- [x] `npx astro add vue`
- [x] Create folder structure
- [x] Add CSS custom properties
- [x] Create TypeScript types
- [x] Setup Vitest + test infrastructure
- [x] **MiniSearch Core Search (Complete)**

### Phase 2: Composables

- [x] useSearch.ts (MiniSearch + fuzzy matching)
- [ ] useCart.ts (migrate CartManager)
- [ ] useModal.ts
- [ ] useNotifications.ts

### Phase 3: Vue Components

- [x] SearchBar.vue (live search with dropdown)
- [ ] CartSidebar.vue
- [ ] TemplateModal.vue
- [ ] FilterBar.vue
- [ ] WizardSection.vue

### Phase 4: Integration

- [ ] Update pages with `client:load` directives
- [ ] Remove ClientScripts.astro
- [ ] Test cart persistence
- [ ] Test all interactions

### Phase 5: Cleanup

- [ ] Remove old neon-\* classes
- [ ] Performance audit
- [ ] Deploy to Cloudflare

## Phase 1: MiniSearch Implementation (COMPLETED)

### What Was Done

**Core Search Infrastructure:**

- Replaced Fuse.js with MiniSearch (saves 4KB bundle)
- Unified search across templates + stacks
- Fuzzy matching with configurable tolerance
- Field boosting for relevance ranking
- Result limiting (max 8 items)

**Files Changed:**

1. `src/composables/useSearch.ts` - Search composable with MiniSearch
2. `src/types/stack.ts` - Stack interface
3. `src/components/vue/SearchBar.vue` - Interactive search UI
4. `package.json` - Added minisearch dependency
5. Test files - 96.87% coverage (3 test files, 854 lines)

### Key Features

**Search Configuration:**

- Fuzzy tolerance: 0.2 (default), 0.3 (for queries ≤3 chars)
- Field boost weights: name (3x), tags (2x), category (1.5x), type (1.5x), description (1x)
- Minimum query length: 2 characters
- Result limit: 8 items max
- Index scope: Templates + Stacks

**Performance Metrics:**

- Index creation: 5-52ms (depends on dataset size)
- Search execution: <1ms per query
- Test coverage: 96.87%

**UI Features (SearchBar.vue):**

- Debounced input (100ms)
- Keyboard navigation (arrow keys, enter, escape)
- Click-outside to close
- "/" key to focus
- Clear button
- Type icon display
- Badge categorization

## Phase 2: UI Components Enhancement (COMPLETED)

### What Was Done

**SearchBar Enhancements:**

- Added Cmd+K / Ctrl+K global keyboard shortcut
- Implemented term highlighting in search results (bold terracotta)
- Added result grouping by type (Templates vs Stacks)
- Integrated stacks data into search

**Files Changed:**

1. `src/components/Hero.astro` - Import stacks, pass to SearchBar
2. `src/components/vue/SearchBar.vue` - Enhanced with new features

### Key Features

**Keyboard Shortcuts:**

- `/` - Focus search input
- `Cmd+K` (Mac) / `Ctrl+K` (Windows/Linux) - Focus and select all text

**Visual Enhancements:**

- Matched terms highlighted in bold with terracotta accent color
- Section headers for "Templates" and "Stacks" when both present
- Regex-safe escaping for special characters in queries

**Architecture:**

- `highlightMatches()` function for term highlighting
- `groupedSuggestions` computed for result grouping
- Proper keyboard navigation across grouped results

**Bundle Impact:**

- SearchBar.vue: 26.57 kB (gzipped: 9.06 kB)
- Build time: ~640ms (client)

**Next Steps:**

- Phase 4: Testing & Validation

## Phase 3: Integration & Polish (COMPLETED)

### What Was Done

**Accessibility Enhancements:**

- Added ARIA attributes (role, aria-expanded, aria-selected, aria-activedescendant)
- Implemented live region for screen reader announcements
- Added `.sr-only` utility class to global.css
- Focus visible ring on search input

**Empty State:**

- Shows when query >= 2 chars with no results
- "No results for 'xyz'" message with icon
- Clickable suggestions: docker, nextjs, security
- role="status" for accessibility

**Mobile Optimization:**

- Dropdown max-height: 60vh (prevents viewport overflow)
- Touch targets: minimum 44px height
- Virtual keyboard compatible
- Responsive layout maintained

**Files Changed:**

1. `src/styles/global.css` - Added sr-only utility
2. `src/components/vue/SearchBar.vue` - ARIA, empty state, mobile fixes

### Key Features

**Empty State UI:**

- Appears when no search results found
- Helpful query suggestions
- Accessible with role="status"

**WCAG AA Compliance:**

- role="combobox" on container
- aria-label on input: "Search templates and stacks..."
- aria-autocomplete="list"
- aria-controls pointing to results listbox
- aria-activedescendant for keyboard selection
- role="listbox" on dropdown
- role="option" with aria-selected on each result
- aria-live="polite" for screen reader announcements

**Mobile UX:**

- max-h-[60vh] prevents dropdown overflow
- min-h-[44px] ensures touch targets
- Focus ring: warm-accent/50 opacity

**Bundle Impact:**

- SearchBar.vue: 28.73 kB (gzipped: 9.69 kB)
- Increase from Phase 2: +2.16 kB (+0.63 kB gzipped)
- Build time: ~585ms

**Accessibility Verification:**
✓ Color contrast 4.5:1
✓ Focus visible on all elements
✓ Keyboard-only navigation
✓ Screen reader announcements
✓ Touch targets >= 44px
✓ ARIA complete

**Next Steps:**

- Phase 4: Testing & Validation
- Cross-browser testing
- Performance benchmarks

### Test Coverage

**Files:**

- `src/__tests__/useSearch.test.ts` - 54 test cases
- `src/__tests__/SearchBar.test.ts` - Component tests
- `src/__tests__/performance.test.ts` - Performance benchmarks

**Coverage Areas:**

- Exact matching (name, stack, type)
- Fuzzy matching (typos, partial, description)
- Minimum character limit
- Result limiting
- Ranking/scoring
- Cross-type search (templates + stacks)
- State management
- Edge cases (empty arrays, special chars, whitespace, case-insensitive, sequential searches)
- Performance (large datasets, search speed)

## Deployment

- Platform: Cloudflare Pages
- Adapter: @astrojs/cloudflare
- Output: Static (with optional server features)

## Related Projects

- `/Applications/MAMP/htdocs/claude-code-templates` - Main CLI tool and component library
- Stacks/outcomes data was researched and created in previous session
