# Widget Development Best Practices for Modyo

## Overview

This guide provides best practices for developing widgets in Modyo, specifically for sites that include Bootstrap 5 by default.

## Bootstrap 5 Integration

### Why Use Bootstrap 5

All Modyo sites created from scratch include Bootstrap 5 by default. This means:
- ✅ **No need to import Bootstrap** - It's already available
- ✅ **Consistent grid system** - Use Bootstrap's responsive grid
- ✅ **Built-in utilities** - Spacing, colors, typography already styled
- ✅ **Smaller CSS** - Leverage existing styles instead of duplicating
- ✅ **Faster development** - Pre-built components and utilities

### Custom Theme Configuration

**Important**: This site uses a **customized Bootstrap 5 theme** with extended design tokens:

**Enhanced Color Palette**:
- **Primary**: Pink/Magenta (`--bs-primary` = `rgb(216, 27, 96)`) - Maps to `--bs-pink-rgb`
- **Secondary**: Purple (`--bs-secondary` = `rgb(72, 72, 183)`) - Maps to `--bs-purple-rgb`
- **Extended shades**: Each color has 25-900 variants (e.g., `--bs-primary-100`, `--bs-primary-600`, etc.)
- **Surface colors**: `--bs-surface-primary`, `--bs-surface-gray`, etc.
- **Soft backgrounds**: `--bs-primary-soft`, `--bs-secondary-soft`, etc.

**Design Tokens**:
- **Extended spacing**: `--bs-ref-spacer-0` through `--bs-ref-spacer-30` (0 to 7.5rem)
- **Rounded borders**: Default `border-radius: 1rem` (pill-shaped buttons)
- **Custom shadows**: Enhanced shadow system for depth
- **Typography**: Source Sans 3 font family with responsive font sizing

### Core Principles

1. **Always use Bootstrap utilities first**
2. **Leverage custom CSS variables** - Use `var(--bs-primary-600)` instead of hard-coded colors
3. **Add custom styles only when necessary**
4. **Don't recreate what Bootstrap already provides**
5. **Keep custom CSS minimal and specific**

## Bootstrap 5 Components to Use

### 1. Grid System

**Always use Bootstrap's grid** for layouts:

```html
<!-- ✅ GOOD: Use Bootstrap grid -->
<div class="container">
  <div class="row g-4">
    <div class="col-12 col-md-6 col-lg-4">
      <!-- Card content -->
    </div>
  </div>
</div>

<!-- ❌ BAD: Custom grid with CSS -->
<div class="custom-container">
  <div class="custom-row">
    <div class="custom-col">
      <!-- Card content -->
    </div>
  </div>
</div>
```

**Grid Classes:**
- `container` / `container-fluid` - Main wrapper
- `row` - Row wrapper
- `col-{breakpoint}-{size}` - Column sizing
- `g-{size}` - Gutter spacing between columns

### 2. Spacing Utilities

**Use Bootstrap spacing** instead of custom margins/paddings:

```html
<!-- ✅ GOOD: Bootstrap spacing -->
<div class="mb-4">Title</div>
<div class="py-5 px-3">Content</div>
<div class="mt-3 mb-5">Footer</div>

<!-- ❌ BAD: Custom spacing -->
<div style="margin-bottom: 24px">Title</div>
<div class="custom-padding">Content</div>
```

**Spacing Scale:**
- `m-{size}` - Margin (0-5, auto)
- `p-{size}` - Padding (0-5)
- `mt-`, `mb-`, `ms-`, `me-` - Margin top, bottom, start, end
- `pt-`, `pb-`, `ps-`, `pe-` - Padding top, bottom, start, end
- `mx-`, `my-` - Margin x-axis, y-axis
- `px-`, `py-` - Padding x-axis, y-axis

### 3. Display & Flexbox

**Use Bootstrap display utilities:**

```html
<!-- ✅ GOOD: Bootstrap flex -->
<div class="d-flex justify-content-between align-items-center">
  <span>Left</span>
  <span>Right</span>
</div>

<div class="d-grid gap-3">
  <button>Button 1</button>
  <button>Button 2</button>
</div>

<!-- ❌ BAD: Custom flexbox CSS -->
<div class="custom-flex-container">
  <span>Left</span>
  <span>Right</span>
</div>
```

### 4. Typography

**Use Bootstrap typography classes:**

```html
<!-- ✅ GOOD: Bootstrap typography -->
<h1 class="display-4 fw-bold">Main Title</h1>
<p class="lead text-muted">Subtitle text</p>
<p class="fs-5 lh-base">Body text</p>

<!-- ❌ BAD: Custom typography CSS -->
<h1 class="custom-title">Main Title</h1>
<p class="custom-subtitle">Subtitle text</p>
```

### 5. Buttons

**Use Bootstrap button styles:**

```html
<!-- ✅ GOOD: Bootstrap buttons -->
<button class="btn btn-primary btn-lg w-100">Primary Action</button>
<a href="#" class="btn btn-outline-secondary">Secondary</a>

<!-- ❌ BAD: Fully custom button styles -->
<button class="custom-button-primary">Primary Action</button>
```

### 6. Cards

**Use Bootstrap card structure:**

```html
<!-- ✅ GOOD: Bootstrap card -->
<div class="card h-100 shadow-sm">
  <img src="..." class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Title</h5>
    <p class="card-text">Description</p>
    <a href="#" class="btn btn-primary w-100">Action</a>
  </div>
</div>

<!-- ❌ BAD: Fully custom card -->
<div class="custom-card">
  <img src="..." class="custom-image">
  <div class="custom-content">
    <h3>Title</h3>
    <p>Description</p>
  </div>
</div>
```

### 7. Colors & Backgrounds

**Use Bootstrap color utilities with extended palette:**

```html
<!-- ✅ GOOD: Bootstrap theme colors -->
<div class="bg-primary text-white">Primary (Pink/Magenta)</div>
<div class="bg-secondary text-white">Secondary (Purple)</div>
<div class="bg-primary-soft">Primary soft background</div>
<div class="bg-surface-gray">Surface gray</div>

<!-- ✅ GOOD: Extended color shades -->
<div class="bg-primary-100 text-primary-700">Light primary bg with dark text</div>
<div class="bg-secondary-600 text-white">Darker secondary</div>

<!-- ✅ GOOD: Semantic colors -->
<div class="bg-success text-white">Success (Green)</div>
<div class="bg-info text-white">Info (Blue)</div>
<div class="bg-warning text-dark">Warning (Yellow)</div>
<div class="bg-danger text-white">Danger (Red)</div>

<!-- ❌ BAD: Custom color classes -->
<div class="custom-pink-bg custom-white-text">Primary background</div>
```

**Available Color Variants** (for each semantic color):
- `-25`, `-50`, `-100` through `-900` (lightness scale)
- `-soft` (very light background)
- `text-{color}-{shade}` (text colors)
- `bg-{color}-{shade}` (background colors)

### 8. Shadows & Borders

**Use Bootstrap shadows and borders:**

```html
<!-- ✅ GOOD: Bootstrap utilities -->
<div class="shadow-sm rounded-3 border border-light">
  Card with shadow and border
</div>

<!-- ❌ BAD: Custom shadow/border CSS -->
<div class="custom-shadow custom-border">
  Card with shadow and border
</div>
```

## Widget Structure Template

### Standard Widget HTML

```html
<div class="widget-name-wrapper">
  <div class="container">
    <!-- Title Section -->
    <div class="text-center mb-5">
      <h2 class="display-5 fw-bold mb-3">Widget Title</h2>
      <p class="lead text-muted">Optional subtitle</p>
    </div>

    <!-- Content Grid -->
    <div class="row g-4">
      <div class="col-12 col-md-6 col-lg-4">
        <div class="card h-100 shadow-sm">
          <img src="..." class="card-img-top" alt="...">
          <div class="card-body d-flex flex-column">
            <h5 class="card-title">Item Title</h5>
            <p class="card-text flex-grow-1">Description</p>
            <a href="#" class="btn btn-primary mt-auto">Action</a>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
```

### Minimal Custom CSS

Only add custom CSS for:
1. Hover effects and transitions (Bootstrap doesn't include these)
2. Component-specific positioning or layout
3. Animation keyframes
4. Complex pseudo-elements

**Use CSS variables** from the theme instead of hard-coded values:

```css
/* ✅ GOOD: Use theme CSS variables */
.widget-name-wrapper {
  background: var(--bs-surface-gray);
  padding: var(--bs-ref-spacer-8) var(--bs-ref-spacer-6);
}

.card {
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-8px);
  box-shadow: var(--bs-box-shadow-lg);
}

.btn-custom {
  background: var(--bs-primary-600);
  color: var(--bs-white);
  border-radius: var(--bs-border-radius-pill);
  padding: var(--bs-ref-spacer-3) var(--bs-ref-spacer-6);
}

.btn-custom:hover {
  background: var(--bs-primary-700);
}

/* ✅ GOOD: Use extended spacing scale */
.section {
  margin-bottom: var(--bs-ref-spacer-12); /* 3rem */
}

/* ❌ BAD: Hard-coded values */
.widget-name-wrapper {
  background: #f0f0f2; /* Use var(--bs-surface-gray) */
  padding: 32px 24px; /* Use var(--bs-ref-spacer-8) var(--bs-ref-spacer-6) */
}

.btn-custom {
  background: #d81b60; /* Use var(--bs-primary) */
  border-radius: 50px; /* Use var(--bs-border-radius-pill) */
}

/* ❌ BAD: Duplicating Bootstrap */
.card {
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  padding: 20px;
  margin-bottom: 20px;
}

.row {
  display: flex;
  flex-wrap: wrap;
}
```

**Key CSS Variables to Use**:

**Colors**:
- `var(--bs-primary)`, `var(--bs-primary-100)` through `var(--bs-primary-900)`
- `var(--bs-secondary)`, `var(--bs-secondary-100)` through `var(--bs-secondary-900)`
- `var(--bs-success)`, `var(--bs-info)`, `var(--bs-warning)`, `var(--bs-danger)`
- `var(--bs-gray-100)` through `var(--bs-gray-900)`
- `var(--bs-surface-primary)`, `var(--bs-surface-gray)`
- `var(--bs-primary-soft)`, `var(--bs-secondary-soft)`

**Spacing** (0.25rem increments):
- `var(--bs-ref-spacer-0)` = 0
- `var(--bs-ref-spacer-4)` = 1rem
- `var(--bs-ref-spacer-8)` = 2rem
- `var(--bs-ref-spacer-12)` = 3rem
- `var(--bs-ref-spacer-16)` = 4rem
- Up to `var(--bs-ref-spacer-30)` = 7.5rem

**Borders**:
- `var(--bs-border-radius)` = 1rem
- `var(--bs-border-radius-pill)` = 50rem
- `var(--bs-border-color)`

**Shadows**:
- `var(--bs-box-shadow-sm)`
- `var(--bs-box-shadow)`
- `var(--bs-box-shadow-lg)`

## Responsive Design with Bootstrap

### Breakpoints

Use Bootstrap's responsive classes:

```html
<!-- Mobile-first approach -->
<div class="col-12 col-sm-6 col-md-4 col-lg-3">
  <!-- 1 column on mobile, 2 on small, 3 on medium, 4 on large -->
</div>

<!-- Hide/show at different breakpoints -->
<div class="d-none d-md-block">Visible on medium and up</div>
<div class="d-block d-md-none">Visible only on small screens</div>
```

### Spacing Responsive

```html
<!-- Different spacing per breakpoint -->
<div class="py-3 py-md-5">
  <!-- py-3 on mobile, py-5 on medium+ -->
</div>

<div class="mb-3 mb-lg-5">
  <!-- mb-3 on mobile, mb-5 on large+ -->
</div>
```

## Content Integration

### Content Consumption Strategy

**Best Practice**: Always use **Liquid-first approach** for content consumption:

1. ✅ **Primary Method**: Use Liquid templates for initial content load (server-side rendering)
2. ✅ **Secondary Method**: Fetch additional content via JavaScript if needed (pagination, infinite scroll)

**Why Liquid-first?**
- ⚡ **Faster initial render** - Content arrives with HTML, no API wait
- 🔍 **Better SEO** - Search engines see actual content, not loading states
- 📱 **Better UX** - Users see content immediately, no spinners
- 💰 **Lower API costs** - Fewer API calls from client-side
- 🔒 **More secure** - No API tokens exposed to browser

### Accessing Modyo Content with Liquid

**Basic pattern** for displaying content:

```liquid
{%- assign entries = spaces['space-uid'].types['type-uid'].entries -%}
{%- if entries.size > 0 -%}
  <div class="container">
    <div class="row g-4">
      {%- for entry in entries -%}
        <div class="col-12 col-md-6 col-lg-4">
          <div class="card h-100 shadow-sm">
            <div class="card-body">
              <h5 class="card-title">{{ entry.fields['Title'] }}</h5>
              <p class="card-text">{{ entry.fields['Description'] }}</p>
              {%- if entry.fields['URL'] != blank -%}
                <a href="{{ entry.fields['URL'] }}" class="btn btn-primary w-100">
                  Read More
                </a>
              {%- endif -%}
            </div>
          </div>
        </div>
      {%- endfor -%}
    </div>
  </div>
{%- else -%}
  <div class="container">
    <div class="alert alert-info text-center">
      No content available
    </div>
  </div>
{%- endif -%}
```

### Hybrid Approach: Liquid + JavaScript

**When to fetch more content via JavaScript:**
- Pagination ("Load More" button)
- Infinite scroll
- Filtering/search after page load
- Real-time updates

**Example**: Initial 12 items via Liquid, load more via JavaScript:

```liquid
<!-- HTML: Render first 12 entries via Liquid -->
{%- assign initial_entries = spaces['products'].types['product'].entries | sort_by: 'created_at' | limit: 12 -%}
<div id="products-grid" class="row g-4">
  {%- for entry in initial_entries -%}
    <div class="col-12 col-md-6 col-lg-4">
      <div class="card h-100 shadow-sm">
        <div class="card-body">
          <h5 class="card-title">{{ entry.fields['Title'] }}</h5>
          <p class="card-text">{{ entry.fields['Description'] }}</p>
        </div>
      </div>
    </div>
  {%- endfor -%}
</div>

<!-- Load More button -->
{%- if initial_entries.size >= 12 -%}
  <div class="text-center mt-4">
    <button id="load-more-btn" class="btn btn-primary">
      Load More Products
    </button>
  </div>
{%- endif -%}
```

```javascript
// JavaScript: Fetch more entries on button click
let page = 2; // Liquid loaded page 1
const loadMoreBtn = document.getElementById('load-more-btn');

loadMoreBtn.addEventListener('click', async () => {
  const response = await fetch(
    `/api/content/spaces/products/types/product/entries?per_page=12&page=${page}`
  );
  const data = await response.json();

  // Append new entries to grid
  const grid = document.getElementById('products-grid');
  data.entries.forEach(entry => {
    const col = document.createElement('div');
    col.className = 'col-12 col-md-6 col-lg-4';
    col.innerHTML = `
      <div class="card h-100 shadow-sm">
        <div class="card-body">
          <h5 class="card-title">${entry.fields.Title}</h5>
          <p class="card-text">${entry.fields.Description}</p>
        </div>
      </div>
    `;
    grid.appendChild(col);
  });

  page++;

  // Hide button if no more entries
  if (data.entries.length < 12) {
    loadMoreBtn.style.display = 'none';
  }
});
```

### Accessing Site Assets

**Important**: Assets can be uploaded to **two locations**:

1. **Content Space Assets** (`/api/admin/content/spaces/{space_id}/assets`)
   - Organized by space
   - Managed in Content module
   - Accessible via Content Delivery API

2. **Site Assets** (`/api/admin/sites/{site_id}/assets`)
   - Organized by site
   - Managed in Channels module
   - Accessible via site URL

Both asset types are CDN-served and can be referenced in Liquid templates.

**Remember**: Field names must match exactly (see [Liquid Field Access](../../channels/liquid/field-access.md))

## Common Patterns

### Hero Section

```html
<div class="hero-wrapper bg-primary text-white">
  <div class="container py-5">
    <div class="row align-items-center">
      <div class="col-lg-6">
        <h1 class="display-3 fw-bold mb-4">Hero Title</h1>
        <p class="lead mb-4">Hero description text</p>
        <a href="#" class="btn btn-light btn-lg">Get Started</a>
      </div>
      <div class="col-lg-6">
        <img src="..." class="img-fluid rounded" alt="Hero">
      </div>
    </div>
  </div>
</div>
```

### Feature Cards

```html
<div class="container py-5">
  <div class="row g-4">
    <div class="col-md-4">
      <div class="text-center">
        <div class="mb-3">
          <!-- Icon or SVG -->
        </div>
        <h5 class="fw-bold">Feature Title</h5>
        <p class="text-muted">Feature description</p>
      </div>
    </div>
  </div>
</div>
```

### Call-to-Action

```html
<div class="cta-wrapper bg-light py-5">
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-lg-8 text-center">
        <h2 class="display-5 fw-bold mb-3">Ready to get started?</h2>
        <p class="lead text-muted mb-4">Join thousands of happy customers</p>
        <a href="#" class="btn btn-primary btn-lg">Start Now</a>
      </div>
    </div>
  </div>
</div>
```

## Performance Tips

1. **Minimize custom CSS** - Less CSS = faster page load
2. **Use Bootstrap classes** - Already cached in browser
3. **Avoid !important** - Breaks Bootstrap cascade
4. **Use Bootstrap variables** - For consistent theming
5. **Keep specificity low** - Easier to maintain

## Common Mistakes

### ❌ Don't Do This

```html
<!-- Don't recreate the grid -->
<div class="custom-container">
  <div class="custom-row">
    <div class="custom-col-4">Content</div>
  </div>
</div>

<!-- Don't duplicate spacing -->
<div style="margin-top: 20px; margin-bottom: 20px;">
  Content
</div>

<!-- Don't ignore Bootstrap utilities -->
<div class="my-custom-centered-flex-container">
  Content
</div>
```

### ✅ Do This Instead

```html
<!-- Use Bootstrap grid -->
<div class="container">
  <div class="row">
    <div class="col-md-4">Content</div>
  </div>
</div>

<!-- Use Bootstrap spacing -->
<div class="my-4">
  Content
</div>

<!-- Use Bootstrap utilities -->
<div class="d-flex justify-content-center align-items-center">
  Content
</div>
```

## Checklist for Widget Development

Before publishing a widget, verify:

**Bootstrap Usage**:
- [ ] Uses Bootstrap grid system (`container`, `row`, `col-*`)
- [ ] Uses Bootstrap spacing utilities (`m-*`, `p-*`, `g-*`)
- [ ] Uses Bootstrap display utilities (`d-flex`, `d-grid`, etc.)
- [ ] Uses Bootstrap typography classes (`display-*`, `lead`, `fs-*`)
- [ ] Uses Bootstrap color utilities (`bg-*`, `text-*`)
- [ ] Uses extended color shades when needed (`bg-primary-100`, `text-secondary-600`)
- [ ] No duplicate styles already in Bootstrap

**CSS Variables**:
- [ ] Uses theme CSS variables instead of hard-coded colors
- [ ] Uses `var(--bs-primary)`, `var(--bs-primary-600)` etc. for colors
- [ ] Uses `var(--bs-ref-spacer-*)` for spacing in custom CSS
- [ ] Uses `var(--bs-border-radius)`, `var(--bs-box-shadow-*)` for effects
- [ ] Custom CSS is minimal and necessary

**Quality & Testing**:
- [ ] Responsive at all breakpoints (xs, sm, md, lg, xl)
- [ ] Field names match Content Type exactly (if using content)
- [ ] Tested on mobile, tablet, and desktop
- [ ] No console errors
- [ ] Hover states work correctly
- [ ] Follows accessibility best practices

## Reference

### Bootstrap 5 Documentation
- [Grid System](https://getbootstrap.com/docs/5.3/layout/grid/)
- [Utilities](https://getbootstrap.com/docs/5.3/utilities/spacing/)
- [Components](https://getbootstrap.com/docs/5.3/components/alerts/)
- [Content](https://getbootstrap.com/docs/5.3/content/typography/)

### Modyo Documentation
- [Liquid Templates](https://docs.modyo.com/en/platform/channels/liquid-markup.html)
- [Widgets](https://docs.modyo.com/en/platform/channels/widgets.html)
- [Content Access](../../channels/liquid/field-access.md)

---

## CSS Variable Reference

### Complete Color System

**Primary (Pink/Magenta)**:
```css
--bs-primary: rgb(216, 27, 96)
--bs-primary-25 through --bs-primary-900 (lightness scale)
--bs-primary-soft: rgb(253, 244, 247)
```

**Secondary (Purple)**:
```css
--bs-secondary: rgb(72, 72, 183)
--bs-secondary-25 through --bs-secondary-900
--bs-secondary-soft: rgb(246, 246, 251)
```

**Semantic Colors**:
- Success (Green): `--bs-success` = `rgb(25, 135, 84)`
- Info (Blue): `--bs-info` = `rgb(13, 110, 253)`
- Warning (Yellow): `--bs-warning` = `rgb(255, 193, 7)`
- Danger (Red): `--bs-danger` = `rgb(220, 53, 69)`

**Gray Scale**:
```css
--bs-gray-25: rgb(251, 251, 252)
--bs-gray-50: rgb(240, 240, 242)
--bs-gray-100: rgb(225, 225, 230)
--bs-gray-200 through --bs-gray-900
```

**Surface & Soft Backgrounds**:
```css
--bs-surface-gray: rgb(240, 240, 242)
--bs-surface-primary: rgb(251, 232, 239)
--bs-surface-secondary: rgb(237, 237, 248)
```

### Spacing Scale Reference

```css
--bs-ref-spacer-0: 0
--bs-ref-spacer-1: 0.25rem
--bs-ref-spacer-2: 0.5rem
--bs-ref-spacer-3: 0.75rem
--bs-ref-spacer-4: 1rem (16px)
--bs-ref-spacer-5: 1.25rem
--bs-ref-spacer-6: 1.5rem
--bs-ref-spacer-7: 1.75rem
--bs-ref-spacer-8: 2rem (32px)
--bs-ref-spacer-12: 3rem (48px)
--bs-ref-spacer-16: 4rem (64px)
--bs-ref-spacer-20: 5rem (80px)
--bs-ref-spacer-24: 6rem (96px)
--bs-ref-spacer-30: 7.5rem (120px)
```

### Quick Reference Examples

**Using color shades**:
```css
.light-bg { background: var(--bs-primary-100); }
.medium-bg { background: var(--bs-primary-500); }
.dark-bg { background: var(--bs-primary-700); }
```

**Using spacing**:
```css
.section { padding: var(--bs-ref-spacer-8); } /* 2rem = 32px */
.gap { gap: var(--bs-ref-spacer-4); } /* 1rem = 16px */
```

---

**Document Version**: 2.0.0
**Last Updated**: 2025-01-10
**Purpose**: Best practices for widget development with customized Bootstrap 5 theme in Modyo
