Themes
Overview
ValPress themes are the presentation layer of the CMS, designed to be lightweight, performant, and deeply integrated with Laravel's Blade engine. Unlike
traditional WordPress themes, ValPress themes follow a structured directory layout that separates logic, views, and assets, while maintaining compatibility with
familiar concepts like functions.php and template tags.
Why It Matters
A well-structured theme system is critical for balancing performance with flexibility. By leveraging Laravel's native tools (Blade, Service Providers, and Routing) within a theme, developers can:
- Improve Performance: Themes are compiled and cached using Laravel's optimized view engine.
- Maintainability: Clear separation of concerns between
src/(logic),views/(presentation), andres/(assets). - Extensibility: Support for child themes allows for safe customization without modifying parent code.
- Developer Experience: Use modern PHP and Blade features instead of complex procedural code.
How It Works
The ThemeManager is responsible for discovering, validating, and booting the active theme. During the ValPress boot sequence, the system:
- Detection: Locates the active theme in
public/themes/{slug}. - Child Theme Check: If an active child theme is defined, it boots the child theme alongside its parent.
- Namespace Registration: Registers the theme's
views/directory into Laravel's view search path, giving the theme precedence over core views. - Resource Registration: Automatically maps
routes/,lang/,src/Controllers, andsrc/Modelsif they exist. - Logic Execution: Requires
functions.phpto allow the theme to register hooks and filters. - Hierarchy Resolution: When a controller requests a view (e.g.,
view('post')), ValPress searches the theme first, then the parent theme, then the core.
Usage
Theme Structure
A standard ValPress theme resides in public/themes/{slug} and follows this layout:
my-theme/
├── config.php # Metadata and parent theme definition
├── functions.php # Hook registration and theme logic
├── screenshot.png # Large preview (1200x900)
├── thumbnail.png # Small preview (300x225)
├── views/ # Blade templates
├── res/ # Assets (CSS, JS, Images)
├── src/ # Custom PHP logic (Controllers, Models)
├── lang/ # Translation files
├── routes/ # Custom theme-specific routes
└── tests/ # PHPUnit tests (required for Marketplace publication)
Creating a New Theme
To create a theme, start with a config.php file containing the required metadata:
<?php
return [
'name' => 'My Premium Theme',
'version' => '1.0.0',
'author' => 'ValPress Team',
'short_description' => 'A clean, modern blog theme.',
'screenshot' => 'screenshot.png',
'thumbnail' => 'thumbnail.png',
];
Template Tags
ValPress provides helper functions (Template Tags) to retrieve data within your Blade templates:
valpress_get_permalink($post): Returns the absolute URL to a post.valpress_get_post_excerpt($post, $length = 20): Returns a truncated version of the post content.the_featured_image($post, $size = 'large'): Returns the URL of the post's featured image.body_class(): Returns a space-separated string of CSS classes for the<body>tag.
Code Examples
1. The functions.php File
Use this file to register styles, scripts, and custom hooks.
<?php
// Enqueue theme assets
add_action('admin_enqueue_scripts', function () {
ScriptManager::enqueue('my-theme-admin', asset('themes/my-theme/res/admin.js'));
});
// Register a custom footer message
add_filter('valpress_footer_text', function ($text) {
return $text . ' | Powered by My Premium Theme';
});
// Register a theme-specific sidebar
add_action('valpress_init', function() {
register_sidebar([
'id' => 'primary-sidebar',
'name' => __('Primary Sidebar'),
'description' => __('Main sidebar for blog posts.'),
]);
});
2. A Basic Blade Template (views/post.blade.php)
@extends('layouts.app')
@section('content')
<article @class(['post-item', 'featured' => $post->is_featured])>
<header>
<h1>{{ $post->post_title }}</h1>
<img src="{{ the_featured_image($post, 'large') }}" alt="{{ $post->post_title }}">
</header>
<div class="entry-content">
{!! apply_filters('the_content', $post->post_content) !!}
</div>
<footer>
<p>Posted in {{ $post->category->name }} on {{ $post->created_at->format('M d, Y') }}</p>
</footer>
</article>
@endsection
3. Child Theme Configuration
To create a child theme, specify the template key in your config.php:
<?php
return [
'name' => 'My Child Theme',
'template' => 'parent-theme-slug', // Directory name of the parent theme
'version' => '1.0.0',
// ...
];
Advanced Usage
Theme-Specific Routes
You can define custom routes inside routes/web.php that only exist when the theme is active. This is useful for landing pages or custom AJAX endpoints.
// public/themes/my-theme/routes/web.php
use Illuminate\Support\Facades\Route;
Route::get('/special-offer', function() {
return view('special-offer');
})->middleware('web');
Custom Controllers
For complex logic, place controllers in src/Controllers/ and register them via functions.php or routing. ValPress automatically handles PSR-4
autoloading for the src/ directory within your theme via the Themes\{StudlySlug} namespace (ExtensionAutoloader).
Update validation
Themes can implement ValidatesUpdate on src/Theme.php to participate in the Update Center pipeline. When verifying a core update, ValPress validates the active theme and its parent (for child themes). Theme self-updates run validation during the extension verify stage.
php artisan vp:make-plugin-validation my-theme
The scaffold command targets plugins; for themes, implement src/Theme.php following the same ValidatesUpdate contract. See Plugin Update Validation.
Marketplace Publication Requirements
Themes submitted to the ValPress Marketplace must include an automated test suite. During editorial review, editors verify that your package contains PHPUnit tests and that they pass against a ValPress installation. Submissions without tests, or with failing tests, will not be approved for publication.
Where to Place Tests
Place your theme tests under:
public/themes/{slug}/tests/
ValPress discovers these directories automatically via PSR-4 mappings in composer.json. Use namespaces that match your theme slug (for example,
Themes\MyTheme\Tests\).
What to Cover
At minimum, your test suite should confirm that the theme renders correctly and integrates safely with ValPress. Prioritize coverage for:
- Template rendering: Key views (home, single post, archive, page) return successful responses.
- Theme logic: Custom routes, controllers, and
functions.phphook registrations. - Child themes: Parent/child relationships and template hierarchy resolution, if applicable.
- Assets and hooks: Enqueued scripts or styles, sidebars, and filters such as
the_contentorvalpress_footer_text.
Verify Before You Submit
Run your theme tests from the ValPress project root before packaging your ZIP for upload:
php artisan test public/themes/my-theme/tests
You can also filter by namespace or test name:
php artisan test --filter MyTheme
All tests must pass. For test database setup, PHPUnit configuration, and additional examples, see Testing & Test Database Setup.
Example Theme Test
<?php // public/themes/my-theme/tests/HomePageTest.php
namespace Themes\MyTheme\Tests;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HomePageTest extends TestCase
{
use RefreshDatabase;
public function test_home_page_loads_successfully(): void
{
$response = $this->get(route('home'));
$response->assertStatus(200);
}
public function test_single_post_template_renders(): void
{
$post = \App\Models\Post::factory()->create([
'post_title' => 'Theme Test Post',
'post_status' => 'publish',
'post_type' => 'post',
]);
$response = $this->get(valpress_get_permalink($post));
$response->assertStatus(200);
$response->assertSee('Theme Test Post');
}
}
Include the tests/ directory in your submission ZIP. Marketplace reviewers expect tests to be part of the distributed package, not maintained separately.
Best Practices
- Ship Tests with Your Theme: Treat automated tests as a required deliverable, especially if you plan to publish on the Marketplace.
- Use Hooking: Always use
add_actionandadd_filterinstead of modifying core files. - Escape Output: Use Blade's
{{ $var }}for automatic escaping, and only use{!! $var !!}when you are certain the content is sanitized (e.g., filtered post content). - Relative Assets: Use the
asset()helper or custom theme asset helpers to ensure URLs are correct across environments. - Localization: Always wrap strings in
__()ortrans()to ensure your theme is translatable.
Common Mistakes
- Direct Database Queries: Avoid
DB::table()inside views. Use models or repository classes insrc/. - Hardcoding URLs: Never hardcode the site URL or theme paths; use
valpress_get_permalink()andasset(). - Missing Screenshots: Forgetting
screenshot.pngmakes your theme look "broken" in the Admin Dashboard. - Ignoring Child Themes: Not checking for parent theme existence when creating a child theme can cause fatal errors.
- Submitting Without Tests: Uploading a Marketplace submission with no
tests/directory, or with failing tests, will delay or block approval. Runphp artisan testlocally before every upload.
Summary
ValPress themes offer a modern, Laravel-powered approach to CMS design. By combining the flexibility of functions.php with the power of Blade and PSR-4
autoloading, you can build highly performant and maintainable frontend experiences. Remember to leverage the ThemeManager's automatic resource registration to
keep your code organized. If you intend to distribute your theme via the Marketplace, include a passing PHPUnit test suite from the start — tests are a
publication requirement, not an optional extra.