# Theme Development

# Quick Start

Sample Theme Repository: https://github.com/beikeshop/beikeshop-sample-theme

You can download the sample theme first and refer to its directory structure and code for development:

# Download the sample theme
git clone https://github.com/beikeshop/beikeshop-sample-theme.git

# Copy the SampleTheme folder to the plugins/ directory of beikeshop
# Then modify and develop according to the instructions in this document

# I. Theme Architecture Overview

BeikeShop supports developing template themes in the form of plugins. The same functionality can have completely different user interfaces.

# Template Loading Mechanism

The system adopts the following template loading strategy:

  1. Priority is given to the enabled theme template
  2. If a file does not exist in the enabled template, it falls back to the default template file under the themes/default directory

This means you only need to override the template files you want to customize; you do not need to copy all files.

# Core Directory Structure

beikeshop/
├── plugins/                          # Plugin directory
│   └── YourTheme/                    # Your template plugin (folder name, PascalCase)
│       ├── config.json               # Plugin configuration (required)
│       ├── Bootstrap.php             # Plugin bootstrap file (required)
│       ├── Resources/                # Development source files (SCSS/JS)
│       │   └── beike/shop/your_theme/
│       │       ├── css/
│       │       │   ├── bootstrap/bootstrap.scss
│       │       │   └── app.scss
│       │       └── js/
│       │           └── app.js
│       ├── Themes/                   # Blade template files
│       │   └── your_theme/
│       │       ├── layout/
│       │       │   └── master.blade.php
│       │       ├── home.blade.php
│       │       └── ...
│       ├── Static/                   # Static assets (published to public)
│       │   ├── image/
│       │   │   ├── logo.png          # Plugin icon
│       │   │   └── theme.jpg         # Theme preview image
│       │   └── public/
│       │       └── build/beike/shop/your_theme/
│       │           ├── css/
│       │           │   ├── bootstrap.css  # Compiled CSS
│       │           │   └── app.css
│       │           └── js/
│       │               └── app.js
│       └── Seeders/                  # Demo data (optional)
│           └── ThemeSeeder.php
├── themes/                           # System template directory (auto-copied during installation)
│   └── your_theme/                   # Copied from plugins/YourTheme/Themes
└── public/                           # Public resource directory (auto-copied during installation)
    └── plugin/your_theme/            # Copied from plugins/YourTheme/Static

# II. Creating a Theme Plugin

# 2.1 Create the Plugin Directory

Create your template folder under the plugins/ directory, for example, Fashion:

mkdir plugins/Fashion

# 2.2 Create config.json

Create config.json in the plugin root directory. This is the core configuration file for the plugin:

{
    "code": "fashion",
    "name": {
        "zh_cn": "Fashion Theme",
        "en": "Fashion Theme"
    },
    "description": {
        "zh_cn": "A fashionable e-commerce theme",
        "en": "A fashionable e-commerce theme"
    },
    "type": "theme",
    "version": "v1.0.0",
    "icon": "/image/logo.png",
    "theme": "/image/theme.jpg",
    "author": {
        "name": "Your Name",
        "email": "your@email.com"
    }
}

Important Field Descriptions:

Field Description Example
code Template unique identifier, lowercase + underscore naming fashion, my_theme
name Template name, supports multiple languages {"zh_cn": "Fashion Theme", "en": "Fashion Theme"}
type Must be theme theme
icon Plugin icon path, relative to the Static/ directory, size 500×500 /image/logo.png
theme Template preview image path, relative to the Static/ directory, size 740×856 /image/theme.jpg

# 2.3 Create Bootstrap.php

Create Bootstrap.php in the plugin root directory. This is the plugin bootstrap file:

<?php
namespace Plugin\Fashion;

class Bootstrap
{
    public function boot()
    {
        // Add hook logic here
        // For example: add custom CSS/JS to the page
        add_hook_blade('layout.header.code', function ($callback, $output, $data) {
            return view('fashion::shop.custom-code');
        });
    }
}

Naming Rules:

  • namespace must be Plugin\YourPluginFolderName (PascalCase)
  • Class name must be Bootstrap
  • Must contain the boot() method

# III. Developing Template Files

# 3.1 Blade Template Files

Template files are placed under the Themes/ directory. The directory structure should be consistent with themes/default.

Development Workflow:

  1. Copy the template files you need to modify from themes/default to your plugin directory:

    themes/default/home.blade.php
    → plugins/Fashion/Themes/fashion/home.blade.php
    
  2. Only modify the files you need to customize; files that don't need modification can remain default

  3. System loading order:

    • First look for themes/fashion/xxx.blade.php
    • If it doesn't exist, fall back to themes/default/xxx.blade.php

Example Directory Structure:

plugins/Fashion/Themes/fashion/
├── layout/
│   ├── master.blade.php      # Main layout (optional override)
│   ├── header.blade.php      # Header (optional override)
│   └── footer.blade.php      # Footer (optional override)
├── home.blade.php            # Homepage
├── category.blade.php        # Category page
├── product/
│   └── product.blade.php     # Product detail page
└── cart/
    └── cart.blade.php        # Shopping cart page

# 3.2 SCSS/CSS Styles

Style source files are placed under Resources/beike/shop/your_theme/css/.

Directory Structure:

plugins/Fashion/Resources/beike/shop/fashion/css/
├── bootstrap/
│   └── bootstrap.scss        # Bootstrap custom variables
├── app.scss                  # Main style file
├── global.scss               # Global styles
├── header.scss               # Header styles
├── footer.scss               # Footer styles
└── home.scss                 # Homepage styles

app.scss Example:

// Import bootstrap variables
@import 'bootstrap/bootstrap';

// Import module styles
@import 'global';
@import 'header';
@import 'footer';
@import 'home';

# 3.3 JavaScript

JS source files are placed under Resources/beike/shop/your_theme/js/.

Directory Structure:

plugins/Fashion/Resources/beike/shop/fashion/js/
├── app.js                    # Main JS file
├── common.js                 # Common functions
├── header.js                 # Header interactions
└── product.js                # Product page interactions

app.js Example:

import './common';
import './header';
import './product';

# IV. Compilation Configuration (Key Point)

# 4.1 Configure webpack.mix.js

This is the most critical step. When developing a new template, you must configure the webpack.mix.js file.

Open the webpack.mix.js file in the project root directory, find the commented-out template compilation code segment (around lines 30-55), uncomment it and modify themeFileName to your plugin folder name (PascalCase):

/* If developing a new theme, uncomment the following code and set themeFileName to your theme folder name */
const themeFileName = 'Fashion';  // ← Modify to your plugin folder name (PascalCase)
const themeCode = themeFileName.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,'');

// Copy template blade files to the themes directory
if (!mix.inProduction()) {
  mix.copy(`plugins/${themeFileName}/Themes`, 'themes');
}

// Compile template scss/js to public/build
mix.sass(`plugins/${themeFileName}/Resources/beike/shop/${themeCode}/css/bootstrap/bootstrap.scss`, `public/build/beike/shop/${themeCode}/css/bootstrap.css`)
.then(() => {
  fs.copyFileSync(`public/build/beike/shop/${themeCode}/css/bootstrap.css`, `plugins/${themeFileName}/Static/public/build/beike/shop/${themeCode}/css/bootstrap.css`);
});

mix.sass(`plugins/${themeFileName}/Resources/beike/shop/${themeCode}/css/app.scss`, `public/build/beike/shop/${themeCode}/css/app.css`)
.then(() => {
  fs.copyFileSync(`public/build/beike/shop/${themeCode}/css/app.css`, `plugins/${themeFileName}/Static/public/build/beike/shop/${themeCode}/css/app.css`);
});

mix.js(`plugins/${themeFileName}/Resources/beike/shop/${themeCode}/js/app.js`, `public/build/beike/shop/${themeCode}/js/app.js`)
.then(() => {
  fs.copyFileSync(`public/build/beike/shop/${themeCode}/js/app.js`, `plugins/${themeFileName}/Static/public/build/beike/shop/${themeCode}/js/app.js`);
});

Important Notes:

Variable Description Example
themeFileName Plugin folder name, PascalCase naming Fashion, MyTheme
themeCode Auto-converted code (lowercase + underscore) fashion, my_theme

Conversion Rules:

Fashion    → fashion
MyTheme    → my_theme
Fashion4   → fashion4

# 4.2 Compilation Commands

Development Phase (watch file changes, auto-compile):

npm install          # Required for the first time to install dependencies
npm run watch        # Watch mode, auto-compiles after file modification

Production Environment (minified build):

npm run prod         # Minified compilation of CSS/JS

# 4.3 Compilation Output Description

After compilation, the following files will be automatically generated:

public/build/beike/shop/fashion/
├── css/
│   ├── bootstrap.css
│   └── app.css
└── js/
    └── app.js

At the same time, through the .then() callback, the compiled files will be automatically copied to the plugin's Static/public/build/ directory:

plugins/Fashion/Static/public/build/beike/shop/fashion/
├── css/
│   ├── bootstrap.css
│   └── app.css
└── js/
    └── app.js

# 4.4 ⚠️ Must Confirm Compiled Files Are Synced Before Packaging

This is a critical point that many developers easily overlook!

Before packaging and publishing the template, you must ensure:

  1. The compiled CSS/JS files already exist in the Static/public/build/ directory
  2. These files will be packaged with the plugin, so users will have styles after installation

How to ensure:

  • Method 1 (Recommended): Use the auto-copy function configured in webpack.mix.js

    • Uncomment the template compilation code
    • Run npm run prod
    • Compiled files will be automatically copied to the Static/public/build/ directory
  • Method 2 (Manual): Manually copy compiled files

    • Copy files from public/build/beike/shop/your_theme/
    • Paste to plugins/YourTheme/Static/public/build/beike/shop/your_theme/

If you forget this step, users will have no styles after installing the template!


# V. Installing and Enabling the Template

# 5.1 Install the Template

  1. Go to Admin Panel → Plugin List
  2. Find your template plugin
  3. Click Install

During installation, the system will automatically:

  • Copy Blade templates from the Themes/ directory to themes/your_theme/
  • Copy static assets from the Static/ directory to the corresponding public/ directory

# 5.2 Enable the Template

  1. Go to Admin Panel → Design → Template Settings
  2. Find your template and click Enable
  3. Optional: Check "Import Demo Data" (if the template provides Seeders)
  4. Confirm enable

# 5.3 Visit the Storefront

After enabling, visit the storefront to see the effect of the new template.


# VI. Template Data Seeders (Optional)

If your template needs to include demo data (such as homepage layout configuration), you can use Seeders.

# 6.1 Create a Seeder

Create ThemeSeeder.php under the Seeders/ directory:

<?php
namespace Plugin\Fashion\Seeders;

use Beike\Repositories\SettingRepo;
use Illuminate\Database\Seeder;

class ThemeSeeder extends Seeder
{
    public function run()
    {
        $homeSetting = $this->getHomeSetting();
        SettingRepo::update('system', 'base', ['design_setting' => $homeSetting]);
    }

    private function getHomeSetting(): array
    {
        // Return homepage layout configuration
        // Data format references the design_setting value in the settings table
        return [
            // ... your homepage configuration data
        ];
    }
}

# 6.2 How to Get Homepage Configuration Data

  1. Configure the homepage layout in the admin design editor
  2. Go to the database settings table, find the record where name = 'design_setting'
  3. Copy the JSON data from the value field
  4. Use a tool to convert JSON to PHP array: https://uutool.cn/json2php/
  5. Put the converted array into the getHomeSetting() method

# 6.3 Import Data When Enabling

When users enable the template in the admin panel, check the "Import Demo Data" option, and the system will automatically run the Seeder to import data.


# VII. Static Asset Management

# 7.1 Static Directory Structure

plugins/Fashion/Static/
├── image/
│   ├── logo.png          # Plugin icon (displayed in admin plugin list)
│   └── theme.jpg         # Theme preview image (displayed on template settings page)
├── public/
│   └── build/beike/shop/fashion/
│       ├── css/          # Compiled CSS (must include)
│       └── js/           # Compiled JS (must include)
└── catalog/fashion/      # Images used by the template (banner, icon, etc.)
    └── demo/

# 7.2 Accessing Assets

Access static assets in templates:

{{-- Access plugin static files --}}
<img src="{{ plugin_asset('fashion', 'image/logo.png') }}">
<img src="{{ asset('plugin/fashion/catalog/fashion/demo/banner.jpg') }}">

{{-- Access compiled CSS/JS (automatically loaded by the system) --}}
{{ mix('/build/beike/shop/fashion/css/app.css') }}
{{ mix('/build/beike/shop/fashion/js/app.js') }}

# 7.3 Responsive Images and Cached Sizes

Supported version

BeikeShop 3.0.0.12 and later.

image_srcset() generates cached copies of an image at different widths. The browser selects a suitable version based on the displayed width and screen pixel density. Merchants upload once, and both themes and plugins can use this feature.

Shrinking an image with CSS alone still downloads the original large file. Responsive images can reduce download size, save bandwidth, and improve loading speed while keeping images sharp on high-density screens.

# Usage

For example, an image takes the full width on mobile and half the width on desktop:

@php
  $imagePath = 'image/catalog/banner.jpg';
@endphp

<img
  src="{{ image_origin($imagePath) }}"
  srcset="{{ image_srcset($imagePath) }}"
  sizes="(min-width: 992px) 50vw, 100vw"
  alt="Promotional banner"
>

Replace $imagePath with your image path, such as $content['image']['src'] from a page builder module.

Pass the original image path relative to public/. Do not convert it to a full URL with image_origin() or asset() first, or pass a thumbnail URL.

Attribute Purpose
src The existing image URL, used as a fallback
srcset Candidate URLs at different widths, generated by image_srcset()
sizes The expected display width, which you specify to match the layout

vw is a percentage of the viewport width. For example, 50vw means half the viewport width. Common values:

Layout sizes
Full-width banner 100vw
One column on mobile, two on desktop (min-width: 992px) 50vw, 100vw
Two columns on mobile, four on desktop (min-width: 992px) 25vw, 50vw
Fixed 600px on desktop, full width on mobile (min-width: 992px) 600px, 100vw

These are estimates. Adjust them for your container width, columns, and breakpoints. The browser uses sizes and pixel density to choose an image; it does not download every candidate.

# Cache Rules

  • Calling image_srcset() generates missing cached sizes. Later requests reuse them. No changes to uploads or the database are needed.
  • Candidate widths are 480, 768, 1024, 1440, and 1920px. Only sizes smaller than the original are generated; the original is the largest candidate.
  • Images keep their aspect ratio, without cropping, padding, or upscaling. A 720px-wide original, for example, only gets a 480px cached copy.
  • Cached files are stored in public/cache/. When the original's modification time advances, cached sizes are regenerated and the ?v= parameter in their URLs is updated.
  • The first request takes extra time to generate missing sizes. Generating files on the server does not mean the browser downloads them.

# Shared Blade Partial (Optional)

You can also output the attributes with design._responsive_image:

<img
  src="{{ image_origin($imagePath) }}"
  @include('design._responsive_image', [
    'image' => ['image_srcset' => image_srcset($imagePath)],
    'sizes' => '(min-width: 992px) 50vw, 100vw',
  ])
  alt="Promotional banner"
>

The partial only outputs attributes; it does not generate cached files. If there are no candidates, it omits srcset and sizes. You can use image_srcset() directly without this partial.

# Notes

  • Supports local JPG, JPEG, PNG, and static WebP images, preserving PNG transparency. Publish bundled plugin images under public/ first.
  • Remote URLs, GIF, animated WebP, SVG, video, and images that cannot be processed return no candidates and continue loading through src.
  • Any theme can use this feature. Existing image_origin(), image_resize(), and image fields remain available, so older themes can keep their existing code.
  • This feature optimizes image file sizes. It does not change the layout, provide separate mobile compositions, or add lazy loading. For images below the fold, use loading="lazy" or your existing lazy loading solution separately.

# VIII. Packaging and Publishing

# 8.1 Pre-Packaging Checklist

  • [ ] All SCSS/JS have been compiled, and compiled files are in Static/public/build/
  • [ ] logo.png and theme.jpg have been replaced with your own assets
  • [ ] config.json information is complete and accurate
  • [ ] Bootstrap.php namespace is correct
  • [ ] Template file paths are correct (Themes/your_theme/)
  • [ ] Test installation, enable, and switch functions are working properly

# 8.2 Packaging

Package the plugin folder as a ZIP archive:

cd plugins
zip -r Fashion.zip Fashion/

# 8.3 Publishing

  1. Log in to the BeikeShop official website
  2. Go to User Center → Plugin Marketplace
  3. Upload the ZIP package to publish
  4. Other users can purchase and install it from the admin plugin marketplace

# IX. Frequently Asked Questions

# Q1: No styles after template installation?

Cause: The compiled CSS/JS files are not included in the plugin's Static/public/build/ directory.

Solution:

  1. Ensure the template compilation code in webpack.mix.js is uncommented
  2. Run npm run prod to compile
  3. Confirm that compiled files exist under plugins/YourTheme/Static/public/build/

# Q2: Modified SCSS but no changes on the storefront?

Cause: Not recompiled or cache not refreshed after compilation.

Solution:

  1. Use npm run watch in the development phase to listen for auto-compilation
  2. Wait a few seconds after modification for auto-compilation to complete
  3. Refresh the browser (may need force refresh Ctrl+Shift+R)

# Q3: Template file modifications not taking effect?

Cause: Blade template cache not cleared.

Solution:

php artisan view:clear
php artisan cache:clear

# Q4: How to override only some template files?

You don't need to copy all files. The system will first look for files under themes/your_theme/, and if they don't exist, fall back to themes/default/. So you only need to override the files you need to modify.

# Q5: What is the difference between themeFileName and themeCode?

  • themeFileName: Plugin folder name, PascalCase naming, e.g., Fashion
  • themeCode: Template unique identifier, lowercase + underscore, e.g., fashion
  • The two are automatically converted via regex; usually you only need to modify themeFileName

# X. Best Practices

  1. Only override needed files: Don't copy all default template files, only override the ones you need to modify
  2. Keep code clean: Follow the project's existing coding style
  3. Manage versions properly: Modify the version in config.json for each release update
  4. Test multilingual support: Ensure the template displays correctly in different languages
  5. Responsive design: Ensure the template displays well on mobile devices
  6. Performance optimization: Use WebP format for images, compress CSS/JS before publishing
  7. Backup data: Back up existing configuration data before enabling a new template

# Appendix: Naming Convention Quick Reference

Item Naming Rule Example
Plugin folder PascalCase Fashion, MyTheme
config.json code lowercase + underscore fashion, my_theme
namespace Plugin\FolderName Plugin\Fashion
themeCode lowercase + underscore fashion
Resource path lowercase + underscore beike/shop/fashion/css/
Blade file lowercase + underscore home.blade.php