create-wp-custom-theme 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ import path from 'path';
7
+ import fs from 'fs-extra';
8
+ import { run } from '../src/index.js';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // Read package.json for version
15
+ const pkgPath = path.resolve(__dirname, '../package.json');
16
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('create-wp-theme')
22
+ .description('Scaffold a production-ready custom WordPress theme')
23
+ .version(pkg.version)
24
+ .argument('<theme-name>', 'Name of the WordPress theme (directory name)')
25
+ .option('--woocommerce', 'Include WooCommerce setup hooks')
26
+ .option('--tailwind', 'Include Tailwind CSS + Vite configuration')
27
+ .action(async (themeName, options) => {
28
+ console.log(chalk.bold.cyan('\n🚀 Welcome to create-wp-theme!\n'));
29
+
30
+ const prompts = [
31
+ {
32
+ type: 'input',
33
+ name: 'author',
34
+ message: 'Author Name:',
35
+ default: 'Your Name'
36
+ },
37
+ {
38
+ type: 'input',
39
+ name: 'description',
40
+ message: 'Theme Description:',
41
+ default: 'A custom WordPress theme.'
42
+ },
43
+ {
44
+ type: 'confirm',
45
+ name: 'git',
46
+ message: 'Initialize a Git repository?',
47
+ default: true
48
+ }
49
+ ];
50
+
51
+ const answers = await inquirer.prompt(prompts);
52
+
53
+ const config = {
54
+ themeName,
55
+ textDomain: themeName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
56
+ author: answers.author,
57
+ description: answers.description,
58
+ git: answers.git,
59
+ woocommerce: options.woocommerce || false,
60
+ tailwind: options.tailwind || false,
61
+ };
62
+
63
+ await run(config);
64
+ });
65
+
66
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "create-wp-custom-theme",
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to scaffold production-ready custom WordPress themes",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-wp-theme": "./bin/create-wp-theme.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [
14
+ "wordpress",
15
+ "theme",
16
+ "scaffold",
17
+ "generator",
18
+ "cli"
19
+ ],
20
+ "author": "",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "chalk": "^5.6.2",
24
+ "commander": "^14.0.3",
25
+ "fs-extra": "^11.3.4",
26
+ "inquirer": "^13.3.2"
27
+ }
28
+ }
package/src/git.js ADDED
@@ -0,0 +1,22 @@
1
+ import { execSync } from 'child_process';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { logger } from './logger.js';
5
+
6
+ export async function initGit(targetPath) {
7
+ try {
8
+ execSync('git init', { cwd: targetPath, stdio: 'ignore' });
9
+
10
+ // Create a basic .gitignore
11
+ const gitignoreContent = `
12
+ node_modules/
13
+ .DS_Store
14
+ dist/
15
+ .env
16
+ `;
17
+ await fs.writeFile(path.join(targetPath, '.gitignore'), gitignoreContent.trim());
18
+ logger.success('Initialized a git repository.');
19
+ } catch (error) {
20
+ logger.warn('Failed to initialize git repository.');
21
+ }
22
+ }
package/src/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import path from 'path';
2
+ import { createThemeDir, copyTemplate, replacePlaceholders } from './scaffold.js';
3
+ import { setupWooCommerce } from './woocommerce.js';
4
+ import { setupTailwind } from './tailwind.js';
5
+ import { initGit } from './git.js';
6
+ import { logger } from './logger.js';
7
+ import chalk from 'chalk';
8
+
9
+ export async function run(config) {
10
+ const targetPath = path.join(process.cwd(), config.themeName);
11
+
12
+ try {
13
+ // 1. Create directory
14
+ await createThemeDir(targetPath);
15
+
16
+ // 2. Copy base templates
17
+ await copyTemplate(targetPath);
18
+
19
+ // 3. Replace Placeholders
20
+ await replacePlaceholders(targetPath, config);
21
+
22
+ // 4. Optional features
23
+ if (config.woocommerce) {
24
+ await setupWooCommerce(targetPath, config.textDomain);
25
+ }
26
+
27
+ if (config.tailwind) {
28
+ await setupTailwind(targetPath, config.themeName);
29
+ }
30
+
31
+ if (config.git) {
32
+ await initGit(targetPath);
33
+ }
34
+
35
+ // 5. Summary
36
+ console.log('');
37
+ logger.success(chalk.bold('Theme scaffolding completed successfully!'));
38
+ console.log(`\nNext steps:\n`);
39
+ console.log(chalk.cyan(` cd ${config.themeName}`));
40
+
41
+ if (config.tailwind) {
42
+ console.log(chalk.cyan(` npm install`));
43
+ console.log(chalk.cyan(` npm run dev`));
44
+ }
45
+
46
+ console.log(`\nHappy coding! 🚀\n`);
47
+
48
+ } catch (error) {
49
+ logger.error(error.message);
50
+ process.exit(1);
51
+ }
52
+ }
package/src/logger.js ADDED
@@ -0,0 +1,9 @@
1
+ import chalk from 'chalk';
2
+
3
+ export const logger = {
4
+ info: (msg) => console.log(chalk.cyan(msg)),
5
+ success: (msg) => console.log(chalk.green('✔ ' + msg)),
6
+ error: (msg) => console.log(chalk.red('✖ ' + msg)),
7
+ warn: (msg) => console.log(chalk.yellow('âš  ' + msg)),
8
+ plain: (msg) => console.log(msg),
9
+ };
@@ -0,0 +1,64 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { logger } from './logger.js';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const templateDir = path.resolve(__dirname, '../template');
9
+
10
+ export async function createThemeDir(targetPath) {
11
+ if (await fs.pathExists(targetPath)) {
12
+ throw new Error(`Folder ${targetPath} already exists. Please choose a different name or delete the folder.`);
13
+ }
14
+ await fs.ensureDir(targetPath);
15
+ logger.success(`Created directory ${targetPath}`);
16
+ }
17
+
18
+ export async function copyTemplate(targetPath) {
19
+ logger.info('Copying theme template files...');
20
+ await fs.copy(templateDir, targetPath);
21
+ logger.success('Template files copied.');
22
+ }
23
+
24
+ async function walkDir(dir) {
25
+ let results = [];
26
+ const list = await fs.readdir(dir);
27
+ for (let file of list) {
28
+ file = path.join(dir, file);
29
+ const stat = await fs.stat(file);
30
+ if (stat && stat.isDirectory()) {
31
+ results = results.concat(await walkDir(file));
32
+ } else {
33
+ results.push(file);
34
+ }
35
+ }
36
+ return results;
37
+ }
38
+
39
+ export async function replacePlaceholders(targetPath, vars) {
40
+ logger.info('Replacing placeholders in template files...');
41
+ const files = await walkDir(targetPath);
42
+
43
+ for (const file of files) {
44
+ // Only process text-based files
45
+ const ext = path.extname(file);
46
+ if (['.png', '.jpg', '.jpeg', '.gif', '.ico'].includes(ext)) {
47
+ continue;
48
+ }
49
+
50
+ try {
51
+ let content = await fs.readFile(file, 'utf8');
52
+
53
+ content = content.replace(/__THEME_NAME__/g, vars.themeName);
54
+ content = content.replace(/__AUTHOR__/g, vars.author);
55
+ content = content.replace(/__DESCRIPTION__/g, vars.description);
56
+ content = content.replace(/__TEXT_DOMAIN__/g, vars.textDomain);
57
+
58
+ await fs.writeFile(file, content, 'utf8');
59
+ } catch (e) {
60
+ // ignore read errors for binaries that might have slipped through
61
+ }
62
+ }
63
+ logger.success('Placeholders replaced dynamically.');
64
+ }
@@ -0,0 +1,83 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { logger } from './logger.js';
4
+
5
+ export async function setupTailwind(targetPath, themeName) {
6
+ logger.info('Setting up Tailwind CSS and Vite...');
7
+
8
+ // 1. Create package.json
9
+ const pkgContent = {
10
+ "name": themeName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
11
+ "version": "1.0.0",
12
+ "scripts": {
13
+ "dev": "vite",
14
+ "build": "vite build"
15
+ },
16
+ "devDependencies": {
17
+ "autoprefixer": "^10.4.16",
18
+ "postcss": "^8.4.31",
19
+ "tailwindcss": "^3.3.5",
20
+ "vite": "^5.0.0"
21
+ }
22
+ };
23
+ await fs.writeFile(path.join(targetPath, 'package.json'), JSON.stringify(pkgContent, null, 2));
24
+
25
+ // 2. Create tailwind.config.js
26
+ const twContent = `/** @type {import('tailwindcss').Config} */
27
+ module.exports = {
28
+ content: [
29
+ './*.php',
30
+ './templates/**/*.php',
31
+ './inc/**/*.php',
32
+ './template-parts/**/*.php',
33
+ './assets/js/**/*.js'
34
+ ],
35
+ theme: {
36
+ extend: {},
37
+ },
38
+ plugins: [],
39
+ }
40
+ `;
41
+ await fs.writeFile(path.join(targetPath, 'tailwind.config.js'), twContent);
42
+
43
+ // 3. Create postcss.config.js
44
+ const postcssContent = `module.exports = {
45
+ plugins: {
46
+ tailwindcss: {},
47
+ autoprefixer: {},
48
+ }
49
+ }
50
+ `;
51
+ await fs.writeFile(path.join(targetPath, 'postcss.config.js'), postcssContent);
52
+
53
+ // 4. Create vite.config.js
54
+ const viteContent = `import { defineConfig } from 'vite';
55
+
56
+ export default defineConfig({
57
+ build: {
58
+ rollupOptions: {
59
+ input: {
60
+ main: './assets/css/main.css',
61
+ script: './assets/js/main.js'
62
+ },
63
+ output: {
64
+ dir: 'dist',
65
+ entryFileNames: '[name].js',
66
+ assetFileNames: '[name].[ext]'
67
+ }
68
+ }
69
+ }
70
+ });
71
+ `;
72
+ await fs.writeFile(path.join(targetPath, 'vite.config.js'), viteContent);
73
+
74
+ // 5. Update main.css with Tailwind directives
75
+ const cssPath = path.join(targetPath, 'assets', 'css', 'main.css');
76
+ if (await fs.pathExists(cssPath)) {
77
+ const twCSS = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n`;
78
+ const existingCss = await fs.readFile(cssPath, 'utf8');
79
+ await fs.writeFile(cssPath, twCSS + existingCss, 'utf8');
80
+ }
81
+
82
+ logger.success('Tailwind CSS and Vite configuration added.');
83
+ }
@@ -0,0 +1,50 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { logger } from './logger.js';
4
+
5
+ export async function setupWooCommerce(targetPath, textDomain) {
6
+ logger.info('Setting up WooCommerce hooks...');
7
+
8
+ // 1. Create inc/woocommerce.php
9
+ const wooCommercePath = path.join(targetPath, 'inc', 'woocommerce.php');
10
+ const wooContent = `<?php
11
+ /**
12
+ * WooCommerce setup and hooks
13
+ *
14
+ * @package \${textDomain}
15
+ */
16
+
17
+ function \${textDomain}_woocommerce_setup() {
18
+ add_theme_support(
19
+ 'woocommerce',
20
+ array(
21
+ 'thumbnail_image_width' => 150,
22
+ 'single_image_width' => 300,
23
+ 'product_grid' => array(
24
+ 'default_rows' => 3,
25
+ 'min_rows' => 1,
26
+ 'default_columns' => 4,
27
+ 'min_columns' => 1,
28
+ 'max_columns' => 6,
29
+ ),
30
+ )
31
+ );
32
+ add_theme_support( 'wc-product-gallery-zoom' );
33
+ add_theme_support( 'wc-product-gallery-lightbox' );
34
+ add_theme_support( 'wc-product-gallery-slider' );
35
+ }
36
+ add_action( 'after_setup_theme', '\${textDomain}_woocommerce_setup' );
37
+ `.replace(/\$\{textDomain\}/g, textDomain);
38
+
39
+ await fs.writeFile(wooCommercePath, wooContent, 'utf8');
40
+
41
+ // 2. Add require line to functions.php
42
+ const functionsPath = path.join(targetPath, 'functions.php');
43
+ if (await fs.pathExists(functionsPath)) {
44
+ let functionsContent = await fs.readFile(functionsPath, 'utf8');
45
+ functionsContent += `\n// Load WooCommerce compatibility file.\nrequire_once get_template_directory() . '/inc/woocommerce.php';\n`;
46
+ await fs.writeFile(functionsPath, functionsContent, 'utf8');
47
+ }
48
+
49
+ logger.success('WooCommerce hooks added.');
50
+ }
@@ -0,0 +1,25 @@
1
+ <?php
2
+ /**
3
+ * The template for displaying 404 pages (not found)
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ get_header();
9
+ ?>
10
+
11
+ <main id="primary" class="site-main">
12
+ <section class="error-404 not-found">
13
+ <header class="page-header">
14
+ <h1 class="page-title"><?php esc_html_e( 'Oops! That page can&rsquo;t be found.', '__TEXT_DOMAIN__' ); ?></h1>
15
+ </header><!-- .page-header -->
16
+
17
+ <div class="page-content">
18
+ <p><?php esc_html_e( 'It looks like nothing was found at this location. Maybe try a search?', '__TEXT_DOMAIN__' ); ?></p>
19
+ <?php get_search_form(); ?>
20
+ </div><!-- .page-content -->
21
+ </section><!-- .error-404 -->
22
+ </main><!-- #primary -->
23
+
24
+ <?php
25
+ get_footer();
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Base CSS File
3
+ */
4
+
5
+ :root {
6
+ --primary-color: #0073aa;
7
+ }
8
+
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
11
+ margin: 0;
12
+ padding: 0;
13
+ color: #333;
14
+ }
15
+
16
+ a {
17
+ color: var(--primary-color);
18
+ text-decoration: none;
19
+ }
20
+
21
+ a:hover {
22
+ text-decoration: underline;
23
+ }
24
+
25
+ img {
26
+ max-width: 100%;
27
+ height: auto;
28
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Main Theme JavaScript
3
+ */
4
+
5
+ document.addEventListener('DOMContentLoaded', () => {
6
+ console.log('__THEME_NAME__ theme JS loaded!');
7
+ });
@@ -0,0 +1,28 @@
1
+ <?php
2
+ /**
3
+ * The template for displaying the footer
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ ?>
9
+ <footer id="colophon" class="site-footer">
10
+ <div class="site-info">
11
+ <a href="<?php echo esc_url( __( 'https://wordpress.org/', '__TEXT_DOMAIN__' ) ); ?>">
12
+ <?php
13
+ /* translators: %s: CMS name, i.e. WordPress. */
14
+ printf( esc_html__( 'Proudly powered by %s', '__TEXT_DOMAIN__' ), 'WordPress' );
15
+ ?>
16
+ </a>
17
+ <span class="sep"> | </span>
18
+ <?php
19
+ /* translators: 1: Theme name, 2: Theme author. */
20
+ printf( esc_html__( 'Theme: %1$s by %2$s.', '__TEXT_DOMAIN__' ), '__THEME_NAME__', '<a href="__AUTHOR_URI__">__AUTHOR__</a>' );
21
+ ?>
22
+ </div><!-- .site-info -->
23
+ </footer><!-- #colophon -->
24
+ </div><!-- #page -->
25
+
26
+ <?php wp_footer(); ?>
27
+ </body>
28
+ </html>
@@ -0,0 +1,14 @@
1
+ <?php
2
+ /**
3
+ * Theme functions and definitions
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ // Include required setup files
13
+ require_once get_template_directory() . '/inc/setup.php';
14
+ require_once get_template_directory() . '/inc/enqueue.php';
@@ -0,0 +1,47 @@
1
+ <?php
2
+ /**
3
+ * The header for our theme
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+ ?>
8
+ <!doctype html>
9
+ <html <?php language_attributes(); ?>>
10
+ <head>
11
+ <meta charset="<?php bloginfo( 'charset' ); ?>">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1">
13
+ <link rel="profile" href="https://gmpg.org/xfn/11">
14
+
15
+ <?php wp_head(); ?>
16
+ </head>
17
+
18
+ <body <?php body_class(); ?>>
19
+ <?php wp_body_open(); ?>
20
+ <div id="page" class="site">
21
+ <header id="masthead" class="site-header">
22
+ <div class="site-branding">
23
+ <?php
24
+ if ( has_custom_logo() ) :
25
+ the_custom_logo();
26
+ else :
27
+ ?>
28
+ <h1 class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
29
+ <?php
30
+ $theme_description = get_bloginfo( 'description', 'display' );
31
+ if ( $theme_description || is_customize_preview() ) :
32
+ ?>
33
+ <p class="site-description"><?php echo $theme_description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
34
+ <?php endif;
35
+ endif;
36
+ ?>
37
+ </div><!-- .site-branding -->
38
+
39
+ <nav id="site-navigation" class="main-navigation">
40
+ <?php
41
+ wp_nav_menu( array(
42
+ 'theme_location' => 'menu-1',
43
+ 'menu_id' => 'primary-menu',
44
+ ) );
45
+ ?>
46
+ </nav><!-- #site-navigation -->
47
+ </header><!-- #masthead -->
@@ -0,0 +1,22 @@
1
+ <?php
2
+ /**
3
+ * Enqueue scripts and styles
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ function __TEXT_DOMAIN___scripts() {
9
+ // Enqueue main stylesheet.
10
+ wp_enqueue_style( '__TEXT_DOMAIN___style', get_stylesheet_uri(), array(), '1.0.0' );
11
+
12
+ // Enqueue base CSS.
13
+ wp_enqueue_style( '__TEXT_DOMAIN___main-style', get_template_directory_uri() . '/assets/css/main.css', array(), '1.0.0' );
14
+
15
+ // Enqueue main JavaScript.
16
+ wp_enqueue_script( '__TEXT_DOMAIN___main-script', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true );
17
+
18
+ if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
19
+ wp_enqueue_script( 'comment-reply' );
20
+ }
21
+ }
22
+ add_action( 'wp_enqueue_scripts', '__TEXT_DOMAIN___scripts' );
@@ -0,0 +1,44 @@
1
+ <?php
2
+ /**
3
+ * Theme setup functions
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ if ( ! function_exists( '__TEXT_DOMAIN___setup' ) ) :
9
+ function __TEXT_DOMAIN___setup() {
10
+ // Add default posts and comments RSS feed links to head.
11
+ add_theme_support( 'automatic-feed-links' );
12
+
13
+ // Let WordPress manage the document title.
14
+ add_theme_support( 'title-tag' );
15
+
16
+ // Enable support for Post Thumbnails on posts and pages.
17
+ add_theme_support( 'post-thumbnails' );
18
+
19
+ // This theme uses wp_nav_menu() in one location.
20
+ register_nav_menus(
21
+ array(
22
+ 'menu-1' => esc_html__( 'Primary Menu', '__TEXT_DOMAIN__' ),
23
+ )
24
+ );
25
+
26
+ // Switch default core markup for search form, comment form, and comments to output valid HTML5.
27
+ add_theme_support(
28
+ 'html5',
29
+ array(
30
+ 'search-form',
31
+ 'comment-form',
32
+ 'comment-list',
33
+ 'gallery',
34
+ 'caption',
35
+ 'style',
36
+ 'script',
37
+ )
38
+ );
39
+
40
+ // Add theme support for selective refresh for widgets.
41
+ add_theme_support( 'customize-selective-refresh-widgets' );
42
+ }
43
+ endif;
44
+ add_action( 'after_setup_theme', '__TEXT_DOMAIN___setup' );
@@ -0,0 +1,31 @@
1
+ <?php
2
+ /**
3
+ * The main template file
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ get_header();
9
+ ?>
10
+
11
+ <main id="primary" class="site-main">
12
+ <?php
13
+ if ( have_posts() ) :
14
+ while ( have_posts() ) :
15
+ the_post();
16
+ // Include the Post-Format-specific template for the content.
17
+ // You can create a template part for this like template-parts/content.php
18
+ the_title( '<h1 class="entry-title">', '</h1>' );
19
+ the_content();
20
+ endwhile;
21
+
22
+ the_posts_navigation();
23
+
24
+ else :
25
+ echo '<p>' . esc_html__( 'No content found', '__TEXT_DOMAIN__' ) . '</p>';
26
+ endif;
27
+ ?>
28
+ </main><!-- #primary -->
29
+
30
+ <?php
31
+ get_footer();
@@ -0,0 +1,41 @@
1
+ <?php
2
+ /**
3
+ * The template for displaying all single pages
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ get_header();
9
+ ?>
10
+
11
+ <main id="primary" class="site-main">
12
+ <?php
13
+ while ( have_posts() ) :
14
+ the_post();
15
+ ?>
16
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
17
+ <header class="entry-header">
18
+ <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
19
+ </header><!-- .entry-header -->
20
+
21
+ <div class="entry-content">
22
+ <?php
23
+ the_content();
24
+ wp_link_pages( array(
25
+ 'before' => '<div class="page-links">' . esc_html__( 'Pages:', '__TEXT_DOMAIN__' ),
26
+ 'after' => '</div>',
27
+ ) );
28
+ ?>
29
+ </div><!-- .entry-content -->
30
+ </article><!-- #post-<?php the_ID(); ?> -->
31
+ <?php
32
+ // If comments are open or we have at least one comment, load up the comment template.
33
+ if ( comments_open() || get_comments_number() ) :
34
+ comments_template();
35
+ endif;
36
+ endwhile; // End of the loop.
37
+ ?>
38
+ </main><!-- #primary -->
39
+
40
+ <?php
41
+ get_footer();
File without changes
@@ -0,0 +1,13 @@
1
+ /*
2
+ Theme Name: __THEME_NAME__
3
+ Theme URI:
4
+ Author: __AUTHOR__
5
+ Author URI:
6
+ Description: __DESCRIPTION__
7
+ Version: 1.0.0
8
+ Tested up to: 6.4
9
+ Requires PHP: 7.4
10
+ Text Domain: __TEXT_DOMAIN__
11
+ License: MIT
12
+ License URI: https://opensource.org/licenses/MIT
13
+ */
@@ -0,0 +1,38 @@
1
+ <?php
2
+ /**
3
+ * Template part for displaying posts
4
+ *
5
+ * @package __THEME_NAME__
6
+ */
7
+
8
+ ?>
9
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
10
+ <header class="entry-header">
11
+ <?php
12
+ if ( is_singular() ) :
13
+ the_title( '<h1 class="entry-title">', '</h1>' );
14
+ else :
15
+ the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
16
+ endif;
17
+ ?>
18
+ </header><!-- .entry-header -->
19
+
20
+ <div class="entry-content">
21
+ <?php
22
+ the_content(
23
+ sprintf(
24
+ wp_kses(
25
+ /* translators: %s: Name of current post. Only visible to screen readers */
26
+ __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', '__TEXT_DOMAIN__' ),
27
+ array(
28
+ 'span' => array(
29
+ 'class' => array(),
30
+ ),
31
+ )
32
+ ),
33
+ wp_kses_post( get_the_title() )
34
+ )
35
+ );
36
+ ?>
37
+ </div><!-- .entry-content -->
38
+ </article><!-- #post-<?php the_ID(); ?> -->