What We Are Building
We are creating a complete classic WordPress theme folder from scratch. By the end of this module you will have a working theme that you can activate in WordPress.
Step 1 — Create the Theme Folder
Navigate to your WordPress installation:
Local Sites → streamvault → app → public → wp-content → themes
Inside themes/ folder, create a new folder:
themes/
├── astra/ ← existing
└── streamvault/ ← create this folder
Open this entire public folder in VS Code.
Step 2 — Create All Required Files
Inside streamvault/ folder, create these files one by one:
streamvault/
├── style.css
├── index.php
├── functions.php
├── header.php
├── footer.php
├── sidebar.php
├── front-page.php
├── page.php
├── single.php
├── archive.php
├── search.php
├── 404.php
└── screenshot.png ← we skip this for now
Open the following folder in Visual Studio Code:
D:\LocalSites\streamvault\app\public\wp-content\themes
Run the following commands in the Visual Studio Code terminal. Copy the entire code below, then paste it into the VS Code terminal all at once and press Enter.
mkdir streamvault cd streamvault
mkdir template-parts, assets, assets\css, assets\js
New-Item style.css -ItemType File New-Item index.php -ItemType File New-Item functions.php -ItemType File New-Item header.php -ItemType File New-Item footer.php -ItemType File New-Item sidebar.php -ItemType File New-Item front-page.php -ItemType File New-Item page.php -ItemType File New-Item single.php -ItemType File New-Item archive.php -ItemType File New-Item search.php -ItemType File New-Item 404.php -ItemType File New-Item template-parts\content-post.php -ItemType File New-Item template-parts\content-none.php -ItemType File New-Item assets\js\main.js -ItemType File
If the streamvault folder already exists
Do not run:
mkdir streamvault
cd streamvault
Instead, navigate into it:
cd streamvault
Then run the remaining commands to create the folders and files inside it.
Step 3 — style.css
This is the most important file. Without the theme header comment, WordPress will not recognize this as a valid theme.
Create style.css:
/* Theme Name: StreamVault Theme URI: https://streamvault.local Author: Gagan Author URI: https://streamvault.local Description: A custom Netflix-style streaming platform theme built from scratch. Version: 1.0.0 Requires at least: 6.0 Requires PHP: 8.0 License: GPL v2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Text Domain: streamvault Tags: custom-theme, streaming, movies, entertainment */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-bg-primary: #141414; --color-bg-secondary: #1f1f1f; --color-bg-card: #2a2a2a; --color-accent: #e50914; --color-accent-hover: #b20710; --color-text-primary: #ffffff; --color-text-secondary: #b3b3b3; --color-text-muted: #757575; --color-border: #333333; --font-primary: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; --container-width: 1280px; --container-padding: 0 40px; --border-radius: 4px; --transition: all 0.3s ease; }
html { font-size: 16px; scroll-behavior: smooth; }
body { font-family: var(--font-primary); background-color: var(--color-bg-primary); color: var(--color-text-primary); line-height: 1.6; min-height: 100vh; }
a { color: var(--color-text-primary); text-decoration: none; transition: var(--transition); }
a:hover { color: var(--color-accent); }
img { max-width: 100%; height: auto; display: block; }
ul { list-style: none; }
.container { max-width: var(--container-width); margin: 0 auto; padding: var(--container-padding); }
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 24px; border: none; border-radius: var(--border-radius); font-size: 1rem; font-weight: 600; cursor: pointer; transition: var(--transition); text-decoration: none; }
.btn-primary { background-color: var(--color-accent); color: var(--color-text-primary); }
.btn-primary:hover { background-color: var(--color-accent-hover); color: var(--color-text-primary); }
.btn-secondary { background-color: rgba(109, 109, 110, 0.7); color: var(--color-text-primary); }
.btn-secondary:hover { background-color: rgba(109, 109, 110, 0.4); color: var(--color-text-primary); }
.section-title { font-size: 1.4rem; font-weight: 700; color: var(--color-text-primary); margin-bottom: 20px; text-transform: uppercase; letter-spacing: 1px; }
.site-main { min-height: 70vh; padding: 40px 0; }
Step 4 — functions.php
This is the brain of the theme. Create functions.php:
<?php
defined('ABSPATH') || exit;
define('STREAMVAULT_VERSION', '1.0.0'); define('STREAMVAULT_DIR', get_template_directory()); define('STREAMVAULT_URI', get_template_directory_uri());
function streamvault_theme_setup() { load_theme_textdomain('streamvault', STREAMVAULT_DIR . '/languages');
add_theme_support('title-tag'); add_theme_support('post-thumbnails'); add_theme_support('html5', [ 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', 'style', 'script', ]); add_theme_support('custom-logo', [ 'height' => 60, 'width' => 200, 'flex-height' => true, 'flex-width' => true, ]); add_theme_support('customize-selective-refresh-widgets');
add_image_size('sv-poster', 300, 450, true); add_image_size('sv-banner', 1280, 720, true); add_image_size('sv-card', 400, 225, true); add_image_size('sv-thumbnail', 150, 150, true);
register_nav_menus([ 'primary' => __('Primary Menu', 'streamvault'), 'footer' => __('Footer Menu', 'streamvault'), 'mobile' => __('Mobile Menu', 'streamvault'), ]); } add_action('after_setup_theme', 'streamvault_theme_setup');
function streamvault_enqueue_assets() { wp_enqueue_style( 'streamvault-style', get_stylesheet_uri(), [], STREAMVAULT_VERSION );
wp_enqueue_script( 'streamvault-main', STREAMVAULT_URI . '/assets/js/main.js', [], STREAMVAULT_VERSION, true );
wp_localize_script('streamvault-main', 'streamvault_data', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('streamvault_nonce'), 'site_url' => get_site_url(), ]); } add_action('wp_enqueue_scripts', 'streamvault_enqueue_assets');
function streamvault_widgets_init() { register_sidebar([ 'name' => __('Main Sidebar', 'streamvault'), 'id' => 'sidebar-main', 'before_widget' => '<div id="%1$s" class="widget %2$s">', 'after_widget' => '</div>', 'before_title' => '<h3 class="widget-title">', 'after_title' => '</h3>', ]);
register_sidebar([ 'name' => __('Footer Column 1', 'streamvault'), 'id' => 'footer-col-1', 'before_widget' => '<div id="%1$s" class="footer-widget %2$s">', 'after_widget' => '</div>', 'before_title' => '<h4 class="footer-widget-title">', 'after_title' => '</h4>', ]);
register_sidebar([ 'name' => __('Footer Column 2', 'streamvault'), 'id' => 'footer-col-2', 'before_widget' => '<div id="%1$s" class="footer-widget %2$s">', 'after_widget' => '</div>', 'before_title' => '<h4 class="footer-widget-title">', 'after_title' => '</h4>', ]);
register_sidebar([ 'name' => __('Footer Column 3', 'streamvault'), 'id' => 'footer-col-3', 'before_widget' => '<div id="%1$s" class="footer-widget %2$s">', 'after_widget' => '</div>', 'before_title' => '<h4 class="footer-widget-title">', 'after_title' => '</h4>', ]); } add_action('widgets_init', 'streamvault_widgets_init');
function streamvault_excerpt_length($length) { return 30; } add_filter('excerpt_length', 'streamvault_excerpt_length');
function streamvault_excerpt_more($more) { return '...'; } add_filter('excerpt_more', 'streamvault_excerpt_more');
Step 5 — header.php
Create header.php:
<!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo('charset'); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <?php wp_head(); ?> </head> <body <?php body_class(); ?>> <?php wp_body_open(); ?>
<header class="site-header"> <div class="container"> <div class="header-inner">
<div class="site-branding"> <?php if (has_custom_logo()): ?> <?php the_custom_logo(); ?> <?php else: ?> <a href="<?php echo esc_url(home_url('/')); ?>" class="site-logo-text"> <?php bloginfo('name'); ?> </a> <?php endif; ?> </div>
<nav class="primary-navigation" aria-label="<?php esc_attr_e('Primary Navigation', 'streamvault'); ?>"> <?php wp_nav_menu([ 'theme_location' => 'primary', 'menu_class' => 'primary-menu', 'container' => false, 'fallback_cb' => false, ]); ?> </nav>
<div class="header-actions"> <a href="<?php echo esc_url(home_url('/search')); ?>" class="header-search-toggle" aria-label="Search"> 🔍 </a> <?php if (is_user_logged_in()): ?> <a href="<?php echo esc_url(wp_logout_url(home_url())); ?>" class="btn btn-secondary"> <?php esc_html_e('Logout', 'streamvault'); ?> </a> <?php else: ?> <a href="<?php echo esc_url(wp_login_url()); ?>" class="btn btn-primary"> <?php esc_html_e('Sign In', 'streamvault'); ?> </a> <?php endif; ?> </div>
</div> </div> </header>
Step 6 — footer.php
Create footer.php:
<footer class="site-footer"> <div class="container">
<div class="footer-top"> <div class="footer-branding"> <a href="<?php echo esc_url(home_url('/')); ?>" class="footer-logo"> <?php bloginfo('name'); ?> </a> <p class="footer-tagline"><?php bloginfo('description'); ?></p> </div>
<div class="footer-widgets"> <?php if (is_active_sidebar('footer-col-1')): ?> <div class="footer-widget-area"> <?php dynamic_sidebar('footer-col-1'); ?> </div> <?php endif; ?>
<?php if (is_active_sidebar('footer-col-2')): ?> <div class="footer-widget-area"> <?php dynamic_sidebar('footer-col-2'); ?> </div> <?php endif; ?>
<?php if (is_active_sidebar('footer-col-3')): ?> <div class="footer-widget-area"> <?php dynamic_sidebar('footer-col-3'); ?> </div> <?php endif; ?> </div> </div>
<div class="footer-bottom"> <p class="footer-copyright"> © <?php echo esc_html(date('Y')); ?> <?php bloginfo('name'); ?>. <?php esc_html_e('All Rights Reserved.', 'streamvault'); ?> </p>
<?php if (has_nav_menu('footer')): ?> <nav class="footer-navigation" aria-label="<?php esc_attr_e('Footer Navigation', 'streamvault'); ?>"> <?php wp_nav_menu([ 'theme_location' => 'footer', 'menu_class' => 'footer-menu', 'container' => false, 'depth' => 1, 'fallback_cb' => false, ]); ?> </nav> <?php endif; ?> </div>
</div> </footer>
<?php wp_footer(); ?> </body> </html>
Step 7 — index.php
This is the ultimate fallback template. Create index.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <?php if (have_posts()): ?> <div class="posts-grid"> <?php while (have_posts()): the_post(); ?> <?php get_template_part('template-parts/content', get_post_type()); ?> <?php endwhile; ?> </div> <?php the_posts_navigation(); ?> <?php else: ?> <?php get_template_part('template-parts/content', 'none'); ?> <?php endif; ?> </div> </main>
<?php get_footer(); ?>
Step 8 — front-page.php
This loads on the homepage. Create front-page.php:
<?php get_header(); ?>
<main class="site-main home-page"> <section class="hero-section"> <div class="container"> <div class="hero-content"> <h1 class="hero-title"> <?php esc_html_e('Unlimited Movies & Series', 'streamvault'); ?> </h1> <p class="hero-subtitle"> <?php esc_html_e('Watch anywhere. Stream anytime. Cancel anytime.', 'streamvault'); ?> </p> <div class="hero-actions"> <a href="<?php echo esc_url(home_url('/movies')); ?>" class="btn btn-primary"> <?php esc_html_e('Browse Movies', 'streamvault'); ?> </a> <a href="<?php echo esc_url(home_url('/series')); ?>" class="btn btn-secondary"> <?php esc_html_e('Browse Series', 'streamvault'); ?> </a> </div> </div> </div> </section> </main>
<?php get_footer(); ?>
Step 9 — page.php
Create page.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <?php while (have_posts()): the_post(); ?> <article id="page-<?php the_ID(); ?>" <?php post_class('single-page'); ?>> <header class="page-header"> <h1 class="page-title"><?php the_title(); ?></h1> </header> <div class="page-content"> <?php the_content(); ?> </div> </article> <?php endwhile; ?> </div> </main>
<?php get_footer(); ?>
Step 10 — single.php
Create single.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <div class="single-post-layout"> <div class="post-content-area"> <?php while (have_posts()): the_post(); ?> <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <header class="entry-header"> <h1 class="entry-title"><?php the_title(); ?></h1> <div class="entry-meta"> <span class="post-date"><?php echo get_the_date(); ?></span> <span class="post-author"> <?php esc_html_e('by', 'streamvault'); ?> <?php the_author(); ?> </span> <span class="post-categories"><?php the_category(', '); ?></span> </div> </header>
<?php if (has_post_thumbnail()): ?> <div class="entry-thumbnail"> <?php the_post_thumbnail('sv-banner'); ?> </div> <?php endif; ?>
<div class="entry-content"> <?php the_content(); ?> </div>
<footer class="entry-footer"> <div class="post-tags"> <?php the_tags('<span class="tags-label">Tags: </span>', ', ', ''); ?> </div> </footer> </article>
<nav class="post-navigation"> <?php the_post_navigation([ 'prev_text' => '← %title', 'next_text' => '%title →', ]); ?> </nav> <?php endwhile; ?> </div>
<aside class="post-sidebar"> <?php get_sidebar(); ?> </aside> </div> </div> </main>
<?php get_footer(); ?>
Step 11 — archive.php
Create archive.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <header class="archive-header"> <h1 class="archive-title"> <?php the_archive_title(); ?> </h1> <?php the_archive_description('<div class="archive-description">', '</div>'); ?> </header>
<?php if (have_posts()): ?> <div class="posts-grid"> <?php while (have_posts()): the_post(); ?> <?php get_template_part('template-parts/content', get_post_type()); ?> <?php endwhile; ?> </div> <?php the_posts_navigation(); ?> <?php else: ?> <?php get_template_part('template-parts/content', 'none'); ?> <?php endif; ?> </div> </main>
<?php get_footer(); ?>
Step 12 — search.php
Create search.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <header class="search-header"> <h1 class="search-title"> <?php printf( esc_html__('Search Results for: %s', 'streamvault'), '<span>' . get_search_query() . '</span>' ); ?> </h1> <?php get_search_form(); ?> </header>
<?php if (have_posts()): ?> <div class="posts-grid"> <?php while (have_posts()): the_post(); ?> <?php get_template_part('template-parts/content', get_post_type()); ?> <?php endwhile; ?> </div> <?php the_posts_navigation(); ?> <?php else: ?> <div class="no-results"> <p><?php esc_html_e('No results found. Try a different search term.', 'streamvault'); ?></p> <?php get_search_form(); ?> </div> <?php endif; ?> </div> </main>
<?php get_footer(); ?>
Step 13 — 404.php
Create 404.php:
<?php get_header(); ?>
<main class="site-main"> <div class="container"> <div class="error-404"> <h1 class="error-code">404</h1> <h2 class="error-title"> <?php esc_html_e('Page Not Found', 'streamvault'); ?> </h2> <p class="error-message"> <?php esc_html_e('The page you are looking for does not exist or has been moved.', 'streamvault'); ?> </p> <a href="<?php echo esc_url(home_url('/')); ?>" class="btn btn-primary"> <?php esc_html_e('Go Back Home', 'streamvault'); ?> </a> </div> </div> </main>
<?php get_footer(); ?>
Step 14 — sidebar.php
Create sidebar.php:
<?php if (is_active_sidebar('sidebar-main')): ?> <aside class="widget-area sidebar-main" aria-label="<?php esc_attr_e('Sidebar', 'streamvault'); ?>"> <?php dynamic_sidebar('sidebar-main'); ?> </aside> <?php endif; ?>
Step 15 — Template Parts Folder
Create this folder structure:
streamvault/
└── template-parts/
├── content-post.php
└── content-none.php
Create template-parts/content-post.php:
<article id="post-<?php the_ID(); ?>" <?php post_class('post-card'); ?>> <?php if (has_post_thumbnail()): ?> <div class="post-card-thumbnail"> <a href="<?php the_permalink(); ?>"> <?php the_post_thumbnail('sv-card'); ?> </a> </div> <?php endif; ?>
<div class="post-card-body"> <div class="post-card-meta"> <span class="post-category"><?php the_category(', '); ?></span> <span class="post-date"><?php echo get_the_date(); ?></span> </div>
<h2 class="post-card-title"> <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> </h2>
<div class="post-card-excerpt"> <?php the_excerpt(); ?> </div>
<a href="<?php the_permalink(); ?>" class="btn btn-primary post-card-link"> <?php esc_html_e('Read More', 'streamvault'); ?> </a> </div> </article>
Create template-parts/content-none.php:
<div class="no-content"> <h2><?php esc_html_e('Nothing Found', 'streamvault'); ?></h2> <p><?php esc_html_e('It seems we cannot find what you are looking for.', 'streamvault'); ?></p> <?php get_search_form(); ?> </div>
Step 16 — Assets Folder
Create this folder structure:
streamvault/
└── assets/
├── css/
└── js/
└── main.js
Create assets/js/main.js:
(function() { 'use strict';
document.addEventListener('DOMContentLoaded', function() { console.log('StreamVault theme loaded'); }); })();
Step 17 — Activate the Theme
Go to Appearance → Themes
You will now see StreamVault in the themes list. Click Activate.
Visit https://streamvault.local — you will see your custom theme running.
Final Folder Structure
streamvault/
├── style.css
├── index.php
├── functions.php
├── header.php
├── footer.php
├── sidebar.php
├── front-page.php
├── page.php
├── single.php
├── archive.php
├── search.php
├── 404.php
├── template-parts/
│ ├── content-post.php
│ └── content-none.php
└── assets/
├── css/
└── js/
└── main.js
Summary
- A valid WordPress theme needs minimum two files —
style.csswith theme header comment andindex.php. functions.phpsets up theme supports, image sizes, menus, widgets, and enqueues assets.header.phpandfooter.phpare included viaget_header()andget_footer().- Template parts in
template-parts/folder are reusable components included viaget_template_part(). - CSS variables in
style.cssdefine the Netflix-dark color scheme for StreamVault. - After creating all files — go to Appearance → Themes → Activate StreamVault.
No comments:
Post a Comment