--- name: cms-component-builder description: Build Optimizely CMS components for Astro v5. Use when creating/modifying components, pages, experiences, or working with GraphQL fragments, opti-type.json, or opti-style.json files (project) --- # CMS Component Builder Build production-ready Optimizely CMS components with proper GraphQL integration, daisyUI styling, and TypeScript type safety. ## When to Use This Skill **ALWAYS use this skill when the user asks to:** - Create a new component, page, or experience (e.g., "create a testimonial component", "add a new page type") - Modify or update `.opti-type.json` files (content type definitions) - Modify or update `.opti-style.json` files (style/display template definitions) - Create or modify GraphQL fragments (`.graphql` or `.dam.graphql` files) - Add components to `allComponents.graphql` or similar aggregation files - Debug component rendering issues - Fix GraphQL type errors related to components - Push/sync content types or styles to the CMS - Understand how the component system works ## Critical Instructions **YOU MUST FOLLOW THESE STEPS IN ORDER:** 1. **READ THE GUIDES FIRST** - This skill includes comprehensive guides that YOU MUST reference: - `CONTENTTYPE-GUIDE.md` - For creating `.opti-type.json` files - `STYLE-GUIDE.md` - For creating `.opti-style.json` files - `GRAPHQL-PATTERNS.md` - For creating `.graphql` and `.dam.graphql` files 2. **EXAMINE EXISTING COMPONENTS** - Always look at similar existing components as examples before creating new ones. Check `src/cms/components/` for patterns. 3. **CREATE ALL REQUIRED FILES** - A complete component needs **5 files minimum**: - `.astro` - Component template with TypeScript - `.opti-type.json` - Content type definition - `.opti-style.json` - Style definition(s) (can have multiple) - `.graphql` - Base GraphQL fragment - `.dam.graphql` - DAM-enabled GraphQL fragment 4. **INTEGRATE PROPERLY** - After creating files: - Add fragment to `src/cms/components/allComponents.graphql` - Push to CMS: `yarn type:push ComponentName` and `yarn style:push StyleName` - Wait 10 seconds for Optimizely Graph sync - Generate types: `yarn codegen` ## Quick Start Example Creating a new "Testimonial" component: ```bash # 1. Create component directory mkdir -p src/cms/components/TestimonialComponent # 2. Create required files src/cms/components/TestimonialComponent/ ├── Testimonial.astro # Component template ├── Testimonial.opti-type.json # Content type definition ├── DefaultTestimonial.opti-style.json # Default style ├── testimonial.graphql # Base GraphQL fragment └── testimonial.dam.graphql # DAM GraphQL fragment ``` ## Required Files Explained Every component needs **at least 5 files**: ### 1. `.astro` - Component Template **IMPORTANT**: Always follow the pattern from existing components like `Button.astro` ```typescript --- import type { TestimonialFragment, DisplaySettingsFragment, } from '../../../../__generated/sdk'; import type { ContentPayload } from '../../../graphql/shared/ContentPayload'; import { isEditContext } from '../../shared/utils.ts'; const isCmsEdit = isEditContext(Astro.url); export interface Props { key: string; data: TestimonialFragment; displaySettings: DisplaySettingsFragment[]; displayTemplateKey: string; contentPayload: ContentPayload; } const { key, data, displaySettings, displayTemplateKey, contentPayload } = Astro.props as Props; // Your styling logic here - reference STYLE-GUIDE.md const componentClass = 'component-testimonial'; ---

{data.Author}

``` **Key Points:** - Import types from `__generated/sdk` (NOT from `@/graphql/__generated/graphql`) - Include ALL required props: `key`, `data`, `displaySettings`, `displayTemplateKey`, `contentPayload` - Use `data-epi-block-id` for CMS editing context - Use `set:html` for rich text fields - Add a component class for CSS targeting ### 2. `.opti-type.json` - Content Type See `CONTENTTYPE-GUIDE.md` for complete guide on creating content types. ### 3. `.opti-style.json` - Style Definitions See `STYLE-GUIDE.md` for complete guide on creating style definitions. ### 4. `.graphql` + `.dam.graphql` - GraphQL Fragments **ALWAYS create both versions.** See `GRAPHQL-PATTERNS.md` for complete details. **Base (`testimonial.graphql`):** ```graphql fragment Testimonial on Testimonial { Quote { html } Author AuthorTitle AuthorImage { ...ContentUrl } } ``` **DAM (`testimonial.dam.graphql`):** ```graphql fragment Testimonial on Testimonial { Quote { html } Author AuthorTitle AuthorImage { ...ContentUrl ...ContentReferenceItem } } ``` ## Component Integration ### 1. Add to allComponents.graphql ```graphql fragment AllComponentsExceptGrid on _IComponent { ...Text ...Button ...Testimonial # Add your component here } ``` **Important:** Add to `AllComponentsExceptGrid`, NOT `AllComponents` ### 2. Sync to CMS & Generate Types ```bash # Push content type and styles to CMS yarn type:push ComponentName yarn style:push StyleName # ⚠️ Wait ~10 seconds for Optimizely Graph to sync # Then generate TypeScript types yarn codegen ``` ### 3. Verify Integration Check `__generated/graphql.ts` for your component type. ## Component Locations - **Components:** `src/cms/components/` - Reusable UI (Button, Card, Hero) - **Pages:** `src/cms/pages/` - Page types (ArticlePage, LandingPage) - **Experiences:** `src/cms/experiences/` - Experience templates - **Compositions:** `src/cms/compositions/` - Layout elements (Row, Column) ## GraphQL Shared Fragments Quick reference (full details in `GRAPHQL-PATTERNS.md`): - `LinkUrl` - Links with url, title, target, text - `ContentUrl` - Content reference URLs - `LinkCollection` - Simple link arrays - `ContentReferenceItem` - DAM metadata (`.dam.graphql` only) - `DisplaySettings` - Display/styling settings - `PageUrl` - Page metadata **Rich text fields:** Always use `{ html }`: ```graphql Body { html } Description { html } ``` ## Styling with daisyUI + TailwindCSS **Prefer daisyUI components:** ```html ``` **Important Svelte 5 Changes:** - Use `$props()` instead of `export let` - Use `$state()` instead of `let` for reactive variables - Use `$derived` instead of `$:` for computed values - Use `$effect()` instead of `$:` for side effects - Use `onclick={}` instead of `on:click={}` - Use `bind:value={}` for two-way binding ### Example: Admin Component with TypeScript See `src/pages/opti-admin/components/_CmsSync.svelte` for a complete example showing: - TypeScript interfaces with Props - State management with `$state()` and `$derived` - Event handling with EventSource (SSE) - Lifecycle with `onMount()` - Conditional rendering with `{#if}` - List rendering with `{#each}` ### Embedding Svelte in Astro ```astro --- // MyPage.astro import MyCoolComponent from './_components/MyCoolComponent.svelte'; const someData = { title: 'Hello', count: 5 }; ---
``` **Client Directives:** - `client:load` - Load immediately on page load - `client:idle` - Load when browser is idle - `client:visible` - Load when component is visible - `client:only="svelte"` - Only run on client, no SSR --- **Remember:** Every component must be production-ready with: - Complete integration into the system - Proper TypeScript types - Consistent daisyUI styling - Both GraphQL fragment versions (.graphql and .dam.graphql) - Comprehensive verification before completion