Visual Portfolio provides multiple ways to customize layouts: CSS variables, WordPress hooks, custom templates, and JavaScript events. This guide covers all customization methods.
Customization Methods
Visual Portfolio offers four primary customization approaches:
- CSS Variables - Quick styling changes
- WordPress Hooks - Modify behavior and output
- Custom Templates - Override default templates
- JavaScript Events - Extend JavaScript functionality
CSS Customization
CSS Variables
Visual Portfolio uses CSS custom properties for easy styling:
/* Override portfolio variables */
.vp-portfolio {
/* Gap between items */
--vp-items-gap: 20px;
/* Item styles */
--vp-item-background: #fff;
--vp-item-border-radius: 8px;
/* Overlay styles */
--vp-overlay-background: rgba(0, 0, 0, 0.8);
--vp-overlay-color: #fff;
/* Typography */
--vp-title-font-size: 20px;
--vp-title-color: #333;
/* Animations */
--vp-transition-duration: 0.3s;
--vp-transition-timing: ease-in-out;
}
Layout-Specific Variables
Slider Layout
.vp-portfolio[data-vp-layout="slider"] {
--vp-layout-slider--auto-items__height: 400px;
--vp-slider-navigation-size: 50px;
--vp-slider-navigation-color: #fff;
--vp-slider-bullets-color: #333;
}
Grid and Masonry
.vp-portfolio[data-vp-layout="grid"],
.vp-portfolio[data-vp-layout="masonry"] {
--vp-grid-columns: 3;
--vp-grid-gap: 20px;
}
Custom CSS Classes
Add custom classes to portfolio blocks:
[visual_portfolio
layout="grid"
class="my-custom-portfolio"
]
Then style with CSS:
.my-custom-portfolio .vp-portfolio__item {
border: 2px solid #ddd;
border-radius: 10px;
}
.my-custom-portfolio .vp-portfolio__item-img {
filter: grayscale(100%);
transition: filter 0.3s;
}
.my-custom-portfolio .vp-portfolio__item:hover .vp-portfolio__item-img {
filter: grayscale(0%);
}
WordPress Hooks
Filter Hooks
Modify Layout Options
// Add custom layout option
add_filter('vpf_registered_layouts', function($layouts) {
$layouts['custom-layout'] = array(
'title' => 'Custom Layout',
'description' => 'My custom layout',
'controls' => array(
// Layout controls
),
);
return $layouts;
});
Modify Item Output
// Filter item classes
add_filter('vpf_each_item_classes', function($classes, $args, $opts) {
if ($args['featured']) {
$classes[] = 'featured-item';
}
return $classes;
}, 10, 3);
Customize Item Meta
// Add custom meta data
add_filter('vpf_each_item_args', function($args, $opts) {
$args['custom_field'] = get_post_meta($args['id'], 'custom_field', true);
return $args;
}, 10, 2);
Action Hooks
Before/After Portfolio
// Add content before portfolio
add_action('vpf_before_portfolio', function($opts) {
echo '<div class="custom-intro">Featured Work</div>';
});
// Add content after portfolio
add_action('vpf_after_portfolio', function($opts) {
echo '<div class="custom-footer">View all work →</div>';
});
Enqueue Custom Assets
// Enqueue custom stylesheet
add_action('wp_enqueue_scripts', function() {
if (has_block('visual-portfolio/block')) {
wp_enqueue_style(
'my-portfolio-custom',
get_stylesheet_directory_uri() . '/portfolio-custom.css'
);
}
});
Custom Templates
Template Hierarchy
Visual Portfolio follows WordPress template hierarchy:
theme/visual-portfolio/
├── items-list/
│ ├── layouts/
│ │ └── slider/
│ │ ├── arrows.php
│ │ ├── bullets.php
│ │ └── thumbnails.php
│ ├── items-style/
│ │ ├── fade/
│ │ │ ├── image.php
│ │ │ └── meta.php
│ │ ├── image.php
│ │ └── meta.php
│ └── item-parts/
│ ├── title.php
│ ├── excerpt.php
│ └── meta-categories.php
└── global/
├── link-start.php
└── link-end.php
Override Templates
- Create directory:
wp-content/themes/your-theme/visual-portfolio/
- Copy template from plugin to theme
- Modify as needed
Example: Custom Title Template
Create: theme/visual-portfolio/items-list/item-parts/title.php
<?php
if (!defined('ABSPATH')) {
exit;
}
if ($opts['show_title'] && $args['title']) : ?>
<h3 class="vp-portfolio__item-title custom-title">
<?php
if ($allow_links) {
visual_portfolio()->include_template('global/link-start', array(
'href' => $args['url'],
'class' => 'vp-portfolio__item-title-link',
));
}
echo esc_html($args['title']);
// Add custom icon
echo ' <span class="custom-icon">★</span>';
if ($allow_links) {
visual_portfolio()->include_template('global/link-end');
}
?>
</h3>
<?php endif; ?>
JavaScript Customization
JavaScript Events
Visual Portfolio triggers jQuery events for customization:
import $ from 'jquery';
// Hook into initialization
$(document).on('initOptions.vpf', (event, self) => {
if (event.namespace !== 'vpf') return;
// Modify options before initialization
console.log('Portfolio options:', self.options);
});
// Hook into layout initialization
$(document).on('initLayout.vpf', (event, self) => {
if (event.namespace !== 'vpf') return;
if (self.options.layout === 'grid') {
// Custom grid modifications
self.addStyle('.vp-portfolio__item-wrap', {
border: '2px solid #ddd',
});
}
});
// After items loaded
$(document).on('afterInitItems.vpf', (event, self) => {
if (event.namespace !== 'vpf') return;
console.log('Items loaded:', self.items.length);
});
// Before Isotope initialization
$(document).on('beforeInitIsotope.vpf', (event, self, initOptions) => {
if (event.namespace !== 'vpf') return;
// Modify Isotope options
initOptions.transitionDuration = '0.5s';
});
// After Isotope initialization
$(document).on('afterInitIsotope.vpf', (event, self) => {
if (event.namespace !== 'vpf') return;
console.log('Isotope initialized');
});
Extend VP Class
// Add custom methods to VP class
$(document).on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') return;
// Add custom method
VP.prototype.customMethod = function() {
const self = this;
console.log('Custom method called on', self.options.layout);
};
});
Access VP Instance
// Access portfolio instance
const $portfolio = $('.vp-portfolio');
if ($portfolio.length && $portfolio[0].vpf) {
const vpInstance = $portfolio[0].vpf;
// Call methods
vpInstance.customMethod();
// Access options
console.log(vpInstance.options);
// Refresh layout
vpInstance.isotope.layout();
}
Advanced Customization Examples
Custom Grid Columns Based on Screen Size
$(document).on('initLayout.vpf', (event, self) => {
if (self.options.layout !== 'grid') return;
// Custom breakpoints
const breakpoints = [
{ width: 1920, columns: 6 },
{ width: 1440, columns: 5 },
{ width: 1024, columns: 4 },
{ width: 768, columns: 3 },
{ width: 480, columns: 2 },
];
breakpoints.forEach(bp => {
self.addStyle('.vp-portfolio__item-wrap', {
width: `${100 / bp.columns}%`,
}, `screen and (max-width: ${bp.width}px)`);
});
});
Add Custom Loading Animation
// Add loading HTML
add_action('vpf_before_portfolio', function($opts) {
echo '<div class="custom-loader"><div class="spinner"></div></div>';
});
/* Loading animation */
.custom-loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 100;
}
.vp-portfolio.vp-loaded .custom-loader {
display: none;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
border-top: 4px solid #333;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Custom Filter Integration
// Custom filter behavior
$(document).on('click', '.custom-filter-button', function() {
const filter = $(this).data('filter');
const $portfolio = $('.vp-portfolio');
if ($portfolio.length && $portfolio[0].vpf) {
const vpInstance = $portfolio[0].vpf;
// Apply filter
if (vpInstance.isotope) {
vpInstance.isotope.arrange({
filter: filter === '*' ? '*' : `.filter-${filter}`,
});
}
}
});
Best Practices
- Use CSS variables for easy theme switching
- Avoid !important unless absolutely necessary
- Test responsive behavior
- Use browser dev tools for debugging
- Keep overrides minimal and maintainable
- Document your changes
- Test after plugin updates
- Use child themes for changes
- Always check event.namespace === ‘vpf’
- Use early returns for better performance
- Test thoroughly before deployment
- Avoid blocking operations
Common Customization Scenarios
Change Default Columns
add_filter('vpf_default_options', function($defaults) {
$defaults['gridColumns'] = 4;
$defaults['masonryColumns'] = 4;
return $defaults;
});
Add Custom Item Classes
add_filter('vpf_each_item_classes', function($classes, $args, $opts) {
// Add category classes
if (!empty($args['categories'])) {
foreach ($args['categories'] as $cat) {
$classes[] = 'category-' . $cat['slug'];
}
}
return $classes;
}, 10, 3);
Modify Image Sizes
add_filter('vpf_each_item_image_size', function($size, $args, $opts) {
// Use larger images for featured items
if ($args['featured']) {
return 'large';
}
return $size;
}, 10, 3);
Debugging
Enable Debug Mode
// Log all VPF events
$(document).on('*.vpf', (event) => {
console.log('VPF Event:', event.type, event);
});
Check Portfolio Instance
const $portfolio = $('.vp-portfolio');
if ($portfolio.length) {
console.log('Portfolio found:', $portfolio[0].vpf);
console.log('Options:', $portfolio[0].vpf.options);
console.log('Items:', $portfolio[0].vpf.items);
} else {
console.log('No portfolio found');
}
Further Reading
Hooks & Filters
Complete list of WordPress hooks
Custom Templates
Template override guide
JavaScript API
JavaScript methods and events
CSS Variables
Complete CSS variables reference
Source Code Reference
- JavaScript Events:
assets/js/layout-*.js
- Template Loading:
classes/class-templates.php
- Hook System:
classes/class-visual-portfolio.php
- CSS Variables:
templates/items-list/items-style/_variables.scss:1