This document outlines best practices for customizing CollectionBuilder-CSV repositories, particularly those enhanced with Oral History as Data (OHD) features. These guidelines help maintain compatibility with the existing infrastructure while enabling powerful customizations.
- Local development:
bundle exec jekyll serve - After config changes: Restart Jekyll server (crucial!)
- Generate thumbnails:
rake generate_derivatives - Large collections:
rake generate_json - Troubleshoot build:
bundle exec jekyll build --verbose - Test site: Visit
http://localhost:4000
- Navigation change: Update
_data/config-nav.csv:1 - Browse customization: Modify
_data/config-browse.csv:1 - Styling: Use Bootstrap classes →
_sass/_custom.scss:1→_data/config-theme-colors.csv:1for bootstrap color changes - New component: Create in
_includes/→ Use_includes/feature/components first - New item type: Add
display_templateto CSV → Create layout in_layouts/item/→ Extend_layouts/item/item-page-base.html:1
- CSV config files (
_data/*.csv) - Existing feature includes (
_includes/feature/*.html) _data/theme.yml:1settings- Custom includes (
_includes/) - Custom CSS (
_sass/_custom.scss:1)
- DON'T create monolithic layouts - extend existing base layouts
- DON'T rebuild Bootstrap components or media embed components - use options in
_includes/feature/
- Check YAML syntax:
_config.yml:1(YAML is space-sensitive - no tabs!) - Verify metadata: No duplicate
objectidvalues in_data/[metadata-file].csv:1 - Check layouts exist: All
display_templatevalues must have matching files in_layouts/item/ - Restart server: After ANY config file changes, restart
bundle exec jekyll serve
- Item pages generate at
/items/{objectid}.htmlvia_plugins/cb_page_gen.rb:34 - Check
objectidvalues are URL-safe (no spaces, special chars) - Verify
display_templatematches layout filename in_layouts/item/
- Navigation processes in
_includes/nav/nav.html:15 - Check
_data/config-nav.csv:1format:display_name,stub,dropdown_parent - Ensure stub URLs start with
/
- Search config:
_data/config-search.csv:1 - Browse config:
_data/config-browse.csv:1 - Metadata config:
_data/config-metadata.csv:1 - Clear browser cache and restart server
- Leverage existing CollectionBuilder and OHD components instead of rebuilding functionality
- Extend existing layouts rather than creating entirely new ones
- Use the modular include system for reusable components
- Use
_data/theme.yml:1for display and styling options on specific pages -- Homepage, browse, subjects, locations, map, timeline, data exports, compound objects behavior and some styles - Minimize custom code when configuration can achieve the same result
- Keep layouts small and focused
- Use
_includesfor reusable components - Build on existing base layouts
- Is it a color change? →
config-theme-colors.csv - Is it Bootstrap-available? → Use Bootstrap classes
- Is it component-specific? →
_sass/_custom.scss - Is it a new feature? → Inline styles in include
- Browse page? →
config-browse.csv - Item metadata? →
config-metadata.csv - Search functionality? →
config-search.csv - Navigation? →
config-nav.csv - Map pop up? →
config-map.csv
- Need an image/video/audio? →
feature/image.html,feature/video.html,feature/audio.html - Need a card/modal/alert? →
feature/card.html,feature/modal.html,feature/alert.html - Need interactive elements? →
feature/accordion.html,feature/button.html - Truly custom component? → Create in
_includes/(last resort)
- Similar to existing type? → Copy and modify existing
_layouts/item/file - Needs audio/video? → Extend from
transcript.htmloraudio.html - Basic display? → Extend from
item.html - Complex features? → Extend from
item-page-base.html
- New page needed? → Create in
pages/with existing layout - Homepage changes? → Modify homepage layout or use
feature/includes - Collection organization? → Update metadata CSV
display_templatefield - Site-wide settings? →
theme.ymlconfiguration
Controls site navigation structure:
display_name,stub,dropdown_parent
About,/about.html
Episodes,/episodes.html
Browse by
Subjects,/subjects.html,Browse by
Map,/map.html,Browse bydisplay_name: Text shown in navigationstub: URL path (relative to site root)dropdown_parent: Groups items under dropdown menus
Configures browse/search page display:
field,display_name,btn,hidden,sort_name
interviewer,Host
date,Date,,,Date
subject,Topics,true
location,,truefield: Metadata field namedisplay_name: Label shown to usersbtn: Show as filter button (true/false)hidden: Hide from display but keep searchablesort_name: Alternative name for sorting
Controls item page metadata display:
field,display_name,browse_link,external_link,dc_map,schema_map
title,Title,,,DCTERMS.title,headline
interviewer,Host,,,DCTERMS.creator,creator
interviewee,Guest,true,,DCTERMS.contributor,contributor
subject,Topics,true,,DCTERMS.subject,keywordsbrowse_link: Make field values clickable links to browse resultsexternal_link: Make field values external linksdc_map&schema_map: SEO and metadata mapping
Defines search functionality:
field,index,display
title,true,true
description,true,true
interviewer,true,false
subject,true,trueindex: Include in search indexdisplay: Show in search results
Bootstrap color theme customization:
color_class,color
primary,#FFD700
secondary,#F4D03F
warning,#B8860BCentral configuration for display options:
# PAGE DISPLAY OPTIONS
featured-image: demo_033 # homepage banner image (objectid/path/URL)
advanced-search: true # enable advanced search modal
browse-buttons: true # previous/next on item pages
subjects-fields: subject;creator # fields for subject cloud page
map-cluster: true # cluster map markers
year-nav-increment: 5 # timeline year navigation
# DATA EXPORTS & SEARCH
metadata-export-fields: "title,creator,date" # CSV download fields
metadata-facets-fields: "subject,creator" # search filter fields
# COMPOUND OBJECTS
browse-child-objects: false # include child objects in browse/search
map-child-objects: true # show child objects on map
# BASIC STYLING
navbar-color: navbar-dark # navbar-light or navbar-dark
bootswatch: # optional Bootswatch theme name
base-font-size: 1.2em # custom font sizingKey Areas: Page behavior, visualization settings, data exports, compound object handling, basic styling.
When to use: Feature toggles, display logic, performance options. Use CSV config files for field-specific settings.
default.html (base HTML structure)
├── page.html (standard page wrapper)
├── item/item-page-base.html (item page foundation)
│ ├── item/transcript.html (OHD transcript layout)
│ ├── item/episode.html (custom podcast layout)
│ ├── item/audio.html (audio file layout)
│ └── item/image.html (image layout)
└── home-podcast.html (custom homepage)
---
layout: item/item-page-base # Build on _layouts/item/item-page-base.html:1
custom-foot: transcript/js/transcript-js.html
---
# Add custom content here
{% include transcript/item/av.html %}
{% include transcript/item/episode-metadata.html %}---
layout: default
---
<!-- 200+ lines of mixed HTML, logic, and styling -->
<div class="massive-single-purpose-layout">
<!-- Everything hardcoded here -->
</div><!-- _layouts/item/episode.html -->
{% include transcript/item/av.html %}
{% include transcript/item/episode-metadata.html %}
{% include transcript/item/bio-modal.html %}<!-- _includes/episode-card.html -->
<div class="episode-card">
<!-- Reusable episode card component -->
</div>
<!-- _includes/transcript/item/episode-metadata.html -->
<div class="episode-metadata">
<!-- Custom metadata display for episodes -->
</div>objectid: Unique identifier (generates/items/{objectid}.html)title: Item title
display_template: Routes to layout in_layouts/item/(e.g., "image", "audio", "video")object_location: Path/URL to media fileimage_thumb: Thumbnail image pathimage_small: Small image path
latitude: For map displaylongitude: For map displaylocation: Place name (searchable)
creator: Author/creatordate: Date (YYYY-MM-DD or YYYY for timeline)description: Item descriptionsubject: Topics (semicolon-separated)format: File typerights: Rights statementidentifier: Original ID
- Any additional columns become automatically searchable
- Configure display in
_data/config-metadata.csv:1 - Enable browse filtering in
_data/config-browse.csv:1
Main collection metadata in _data/metadata.csv:1 (or configured filename in _config.yml:37):
objectid,title,display_template,creator,date,description,subject,object_location
episode001,Episode Title,episode,Host Name,2024-01-01,Description,topic1; topic2,/objects/audio.mp3Key Fields:
objectid: Unique identifier (generates/items/{objectid}.html)display_template: Determines which layout in_layouts/item/to useobject_location: Path to media files- Custom fields: Add any additional metadata columns
CollectionBuilder automatically routes items based on display_template (via _plugins/cb_page_gen.rb:34):
display_template: episode→_layouts/item/episode.html:1display_template: transcript→_layouts/item/transcript.html:1display_template: audio→_layouts/item/audio.html:1
_includes/
├── feature/ # Bootstrap-based reusable components (USE THESE FIRST!)
│ ├── image.html # Responsive image display
│ ├── card.html # Bootstrap card component
│ ├── audio.html # Audio player embed
│ ├── video.html # Video player embed
│ ├── gallery.html # Image gallery
│ ├── accordion.html # Bootstrap accordion
│ ├── modal.html # Bootstrap modal
│ ├── alert.html # Bootstrap alert
│ ├── button.html # Styled buttons
│ ├── jumbotron.html # Hero sections
│ └── nav-menu.html # Custom navigation
├── head/ # HTML head components
├── item/ # Item page components
│ ├── audio-player.html
│ ├── download-buttons.html
│ └── metadata.html
├── index/ # Homepage components
│ ├── carousel.html
│ ├── featured-terms.html
│ └── description.html
└── js/ # JavaScript components
The _includes/feature/ directory is your secret weapon. These pre-built, Bootstrap-styled components should be your first choice for adding content. They handle responsive design, accessibility, and consistent styling automatically.
Image Display (feature/image.html):
{% include feature/image.html objectid="demo_001" %}
{% include feature/image.html objectid="demo_001;demo_002" width="50" %}
{% include feature/image.html objectid="https://example.com/image.jpg" alt="Description" caption="Custom caption" %}- Supports collection objectids, external URLs, or local image paths
- Automatic responsive sizing with width options (25%, 50%, 75%, 100%)
- Built-in accessibility features and captions
Cards (feature/card.html):
{% include feature/card.html text="Card content" header="Card Header" objectid="demo_001" %}
{% include feature/card.html text="Right-aligned card" width="50" float="end" centered="true" %}- Bootstrap card styling with image caps
- Flexible positioning (float, center, width control)
- Header, title, and content areas
Audio/Video Players (feature/audio.html, feature/video.html):
{% include feature/audio.html objectid="demo_audio" %}
{% include feature/video.html objectid="https://youtu.be/VIDEOID" %}
{% include feature/video.html objectid="https://vimeo.com/VIDEOID" ratio="4by3" %}- Supports multiple platforms (YouTube, Vimeo, local files)
- Responsive video embedding with aspect ratio control
Interactive Components:
{% include feature/modal.html title="Modal Title" text="Modal content" button="Open Modal" %}
{% include feature/accordion.html title1="Section 1" text1="Content 1" title2="Section 2" text2="Content 2" %}
{% include feature/alert.html text="Important notice" color="warning" %}
{% include feature/button.html text="Click Me" link="/page.html" color="primary" %}_includes/transcript/
├── item/ # Transcript item components
│ ├── av.html # Audio/video player wrapper
│ ├── bio-modal.html # Guest biography modal
│ ├── metadata.html # Transcript metadata display
│ └── transcript.html # Transcript text processing
├── player/ # Media player components
│ ├── mp3.html
│ ├── youtube.html
│ └── vimeo.html
├── style/ # Styling components
└── js/ # JavaScript functionality
- Use descriptive, hyphenated names:
episode-card.html - Group by functionality:
transcript/item/episode-metadata.html - Include header comments for documentation:
<!-- cb: _includes/episode-card.html -->
<!-- Episode card component for homepage and browse pages -->
<div class="episode-card">
<!-- Component content -->
</div>
<!-- /cb: _includes/episode-card.html -->For Bootstrap color overrides, use _data/config-theme-colors.csv:1:
color_class,color
primary,#FFD700
secondary,#F4D03F
warning,#B8860B
success,#28a745
info,#17a2b8For custom Sass variables, modify assets/css/cb.scss:1:
---
# base CSS for collectionbuilder
---
@charset "utf-8";
/* Custom Sass variables */
$podcast-primary: #FFD700;
$podcast-secondary: #F4D03F;
$episode-border-radius: 12px;
/* Continue with existing base variables... */For component styles and CSS overrides, use _sass/_custom.scss:1:
/* _sass/_custom.scss */
/* Custom component styles - NO color variables here */
.episode-card {
border: 2px solid var(--bs-warning); /* Use Bootstrap CSS variables */
border-radius: 12px;
transition: all 0.3s ease;
&:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
}
/* Responsive utilities */
@media (max-width: 768px) {
.episode-card {
margin-bottom: 1rem;
}
}Why this structure?
config-theme-colors.csv: Bootstrap color theme integrationassets/css/cb.scss: Sass variables and Jekyll processing_sass/_custom.scss: Component styles and CSS overrides- Maintains proper compilation order and compatibility
Only use inline styles for new feature components or one-off custom includes:
<!-- _includes/episode-card.html -->
<div class="episode-card">
<!-- Component content -->
</div>
<style>
/* Component-specific styles for new features only */
.episode-card-specific-feature {
/* Styles that only apply to this component */
}
</style>CollectionBuilder includes Bootstrap 5. Always prefer Bootstrap classes:
<!-- ✅ GOOD: Use Bootstrap classes -->
<div class="row g-4">
<div class="col-lg-4 col-md-6 col-sm-12">
<div class="card border-warning">
<div class="card-body text-center">
<!-- Content -->
</div>
</div>
</div>
</div>
<!-- ❌ AVOID: Custom CSS when Bootstrap exists -->
<div class="custom-grid">
<div class="custom-column">
<div class="custom-card">
<!-- Reinventing Bootstrap -->
</div>
</div>
</div>_sass/
├── _custom.scss:1 # Component styles and CSS overrides
├── _base.scss:1 # CollectionBuilder base styles
├── _ohd.scss:1 # OHD-specific styles
├── _pages.scss:1 # Page-specific styles
└── _theme-colors.scss:1 # Bootstrap color overrides
_data/
└── config-theme-colors.csv:1 # Bootstrap color theme configuration
assets/css/
└── cb.scss:1 # Sass variables and Jekyll processing
CollectionBuilder automatically generates reusable data files in assets/data/ that serve multiple purposes:
assets/data/
├── metadata.csv:1 # Downloadable metadata export
├── metadata.json:1 # JSON version for APIs
├── subjects.csv:1 # Subject terms and counts
├── subjects.json:1 # Subject data for visualizations
├── locations.csv:1 # Location data export
├── geodata.json:1 # Geographic data for maps
├── timelinejs.json:1 # Timeline.js compatible data
├── facets.json:1 # Search facet data
└── collection-info.json:1 # Collection statistics
Download Features:
metadata.csv- Provides "Download Data" functionality on data pagesubjects.csv,locations.csv- Specialized data exports- Generated from
metadata-export-fieldsintheme.yml
Visualization Features:
geodata.json- Powers map visualizationstimelinejs.json- Timeline.js integrationsubjects.json- Subject cloud displaysfacets.json- Advanced search filtering
API Integration:
- All
.jsonfiles provide API endpoints for external tools - Used by some advanced CollectionBuilder features
- Enable integration with external visualization tools
CollectionBuilder uses a powerful pattern of combining Liquid/Jekyll data processing with Bootstrap components and JavaScript libraries to create dynamic, interactive pages.
Example: Timeline Navigation
{%- assign items = site.data[site.metadata] | where_exp: 'item','item.objectid' -%}
{%- assign items = items | where_exp: 'item','item[field]' | sort: field -%}
{%- assign raw-dates = items | map: field | compact | uniq -%}
{%- capture clean-years -%}
{% for date in raw-dates %}
{% if date contains "-" %}{{ date | strip | split: "-" | first }}
{% elsif date contains "/" %}{{ date | strip | split: "/" | last }}
{% else %}{{ date | strip }}{% endif %}
{% unless forloop.last %};{% endunless %}
{% endfor %}
{%- endcapture -%}
{%- assign uniqueYears = clean-years | split: ";" | compact | uniq | sort -%}Key Techniques:
- Data filtering:
where_expfor complex conditions - Data transformation:
map,split,joinfor processing - Liquid capture: Build strings dynamically
- Bootstrap integration: Generate dropdown navigation
Example: Interactive Map
<!-- _layouts/map.html -->
---
layout: default
custom-foot: js/map-js.html
---
<div id="map"></div>
<!-- _includes/js/map-js.html -->
{%- assign items = site.data[site.metadata] | where_exp: 'item','item.latitude != nil and item.longitude != nil' -%}
{%- assign fields = site.data.config-map -%}
<script>
var geodata = { "type": "FeatureCollection", "features": [
{% for item in items %}
{ "type":"Feature",
"geometry":{ "type":"Point", "coordinates":[{{ item.longitude }},{{ item.latitude }}] },
"properties": {
{% for f in fields %}
{% if item[f.field] %}{{ f.field | jsonify }}:{{ item[f.field] | strip | escape | jsonify }},{% endif %}
{% endfor %}
"objectid": {{ item.objectid | jsonify }}
}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]};
// Initialize Leaflet map with generated data
var map = L.map('map').setView([{{ site.data.theme.latitude }}, {{ site.data.theme.longitude }}], {{ site.data.theme.zoom-level }});
// Add markers from geodata...
</script>Key Techniques:
- Liquid preprocessing: Filter and transform data on build
- JSON generation: Create JavaScript data structures with
jsonify - Configuration integration: Use
config-map.csvfor field display - JavaScript libraries: Leaflet maps, clustering, search
- Bootstrap components: Modals, cards for map popups
Example: Dynamic Carousel
<!-- _includes/index/carousel.html -->
{%- assign carousel-max = include.max | default: 9 -%}
{%- if include.child-objects == true -%}
{%- assign items = site.data[site.metadata] | where_exp: 'item','item.objectid' -%}
{%- else -%}
{%- assign items = site.data[site.metadata] | where_exp: 'item','item.objectid and item.parentid == nil' -%}
{%- endif -%}
{% if include.filter-field and include.filter-value %}
{%- assign items = items | where_exp: 'item', 'item[include.filter-field] contains include.filter-value' -%}
{% endif %}
{%- assign items = items | where_exp: 'item','item.image_thumb' | sample: carousel-max -%}
<div id="carouselFade" class="carousel slide carousel-fade" data-bs-ride="carousel">
<div class="carousel-inner">
{% for item in items %}
<div class="carousel-item{% if forloop.first %} active{% endif %}">
<img class="d-block w-100" src="{{ item.image_thumb | relative_url }}" alt="{{ item.title | escape }}">
<div class="carousel-caption">
<h5>{{ item.title }}</h5>
<a href="{{ '/items/' | append: item.objectid | append: '.html' | relative_url }}" class="btn btn-{{ include.btn-color | default: 'primary' }}">
{{ include.btn-text | default: 'View Item' }}
</a>
</div>
</div>
{% endfor %}
</div>
</div>Key Techniques:
- Flexible filtering: Runtime filters via include parameters
- Random sampling:
samplefilter for variety - Bootstrap carousel: Native component with Liquid data
- Configurable styling: Bootstrap color classes via parameters
<!-- 1. Define data processing at top -->
{%- assign items = site.data[site.metadata] | where_exp: 'item','item.objectid' -%}
{%- assign filtered_items = items | where: 'display_template', 'episode' -%}
<!-- 2. Use configuration files -->
{%- assign fields = site.data.config-browse -%}
<!-- 3. Generate clean JSON for JavaScript -->
var episodes = [
{% for item in filtered_items %}
{
"id": {{ item.objectid | jsonify }},
"title": {{ item.title | escape | jsonify }},
"date": {{ item.date | jsonify }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
];
<!-- 4. Integrate with Bootstrap -->
<div class="row">
{% for item in filtered_items %}
<div class="col-md-4">
<div class="card">
<!-- Bootstrap card with Liquid data -->
</div>
</div>
{% endfor %}
</div><!-- Don't hardcode data -->
var episodes = [
{"id": "ep1", "title": "Episode 1"},
{"id": "ep2", "title": "Episode 2"}
];
<!-- Don't bypass configuration -->
<div class="custom-grid">
<!-- Custom HTML instead of Bootstrap -->
</div>
<!-- Don't ignore CollectionBuilder filters -->
{% for item in site.data.metadata %}
<!-- Processing ALL items instead of using CB patterns -->
{% endfor %}Configure exports in _data/theme.yml:70:
# Specify which fields to include in data downloads
metadata-export-fields: "objectid,title,creator,date,description,subject,location,rights"
# Control what gets included in faceted search
metadata-facets-fields: "subject,creator,format"Important Notes:
- These files regenerate automatically when site builds
- Don't edit them directly - they'll be overwritten
- Configure what's included via
theme.ymland config CSV files - For large collections, use
rake generate_jsonfor performance
{%- assign episodes = site.data[site.metadata] | where: "display_template", "episode" -%}
{%- assign featured_count = site.data.theme.featured-episodes | default: 6 -%}{%- assign fields = site.data.theme.episode-fields | split: ',' -%}
{% if site.data.theme.episode-fields contains 'interviewer' %}
<!-- Display interviewer -->
{% endif %}{% assign recent_episodes = site.data[site.metadata]
| where: "display_template", "episode"
| sort: "date"
| reverse
| limit: 6 %}- Add new
display_templatevalue to metadata CSV - Create layout in
_layouts/item/{template-name}.html - Extend from
item-page-base.html - Use existing includes where possible
- Update
config-browse.csvfor field display - Add custom styling in page markdown file
- Use existing browse layout with custom CSS
- Update
config-nav.csv - Create corresponding page in
pages/ - Use appropriate existing layout
Use existing player system:
{% if page.object_location contains 'mp3' %}
{% assign av = "mp3" %}
{% endif %}
{% if av %}
{% include transcript/item/av.html %}
{% endif %}Leverage built-in search:
- Configure in
config-search.csv - Use existing search layout
- Add custom styling as needed
OHD provides powerful visualization features:
- Timeline views
- Subject clouds
- Location maps
- Transcript analysis
Enable through theme.yml configuration rather than rebuilding.
- Build test:
bundle exec jekyll build --verbose - Local preview:
bundle exec jekyll serve→ test athttp://localhost:4000 - Check navigation: All menu items work
- Test search/browse: Verify filtering and results
- Mobile test: Check responsive design
- Page generation: Verify new items appear at
/items/{objectid}.html - Configuration: Test any CSV config changes
- No build errors in terminal
- All pages load without 404s
- Search returns expected results
- New metadata fields display correctly
- ❌ Not restarting server after config changes (
_config.yml,theme.yml, CSV files) - ❌ Creating custom Bootstrap CSS instead of using
_data/config-theme-colors.csv:1 - ❌ Editing auto-generated files in
assets/data/(they get overwritten) - ❌ Using tabs in YAML files (use spaces only)
- ❌ Duplicate
objectidvalues in metadata CSV - ❌ Missing layouts for
display_templatevalues - ❌ Hardcoding paths instead of using Jekyll variables
- ❌ Ignoring build warnings (they often indicate real problems)
- Collections >1000 items: Set
json-generation: truein_data/theme.yml:1 - Large images: Run
rake generate_derivativesfirst - Slow searches: Limit search fields in
_data/config-search.csv:1
- Check
display_templatevalues in metadata CSV - Ensure layout file exists in
_layouts/item/ - Verify
objectidvalues are unique and URL-safe
- Check file paths in
_includes/ - Verify include statements use correct path
- Ensure file extensions are
.html
- Verify CSV files have proper headers
- Check
_data/theme.yml:1syntax (YAML is space-sensitive) - Restart Jekyll development server after config changes
If converting existing hardcoded customizations:
- Extract Reusable Components → Move to
_includes/ - Identify Configuration Options → Move to CSV files or
theme.yml - Simplify Layouts → Use base layouts and includes
- Test Functionality → Ensure all features still work
CollectionBuilder includes powerful Rake tasks for common development and maintenance operations:
rake generate_derivatives- Automatically generates thumbnail and small images from objects/ directory
- Optimizes images for web delivery
- Creates PDF thumbnails from first page
- Handles large collections efficiently
rake rename_by_csv # Bulk rename files using CSV mapping
rake rename_lowercase # Convert all filenames to lowercase
rake resize_images # Batch resize images to specific dimensions
rake download_by_csv # Download remote files listed in CSVrake generate_json- Creates optimized JSON files for visualization features
- Essential for collections with 1000+ items
- Improves page load performance
- Must set
json-generation: trueintheme.ymlfirst
Core functionality: Automatically generates item pages from CSV metadata.
- Reads metadata CSV and creates
/items/{objectid}.htmlpages - Routes pages to appropriate layouts based on
display_templatefield - Handles URL slugification and validation
- Configured via
_config.ymlpage_gensettings
Liquid filters and utilities:
{{ item.subject | array_count_uniq }} # Count unique values in arrays
{{ page.description | strip_html | truncate: 150 }} # Text processing helpersArray manipulation filters for processing metadata fields with multiple values.
- Setting up a new collection with many images
- Migrating from another platform
- Batch processing existing files
- Optimizing site performance
- Changing page generation logic (advanced)
- Adding custom Liquid filters
- Extending metadata processing
# 1. Add images to objects/ directory
# 2. Generate derivatives
rake generate_derivatives
# 3. For large collections, generate JSON
rake generate_json
# 4. Test site locally
bundle exec jekyll serve- Images not processing: Check file permissions and ImageMagick installation
- Large memory usage: Process images in smaller batches
- JSON generation fails: Verify CSV metadata syntax
- Pages not generating: Check
display_templatevalues and layout files - Liquid errors: Verify helper function syntax in templates
This approach maintains flexibility while leveraging CollectionBuilder's robust infrastructure for search, browse, data management, responsive design, and automated content processing.