Menu

从零做一个 InnoCMS 主题:目录结构、Blade 继承、SCSS 编译、Hook 集成

InnoCMS 2026-06-10 260
从零做一个 InnoCMS 主题:目录结构、Blade 继承、SCSS 编译、Hook 集成
拆解主题开发的完整流程:目录结构、Blade 继承、SCSS 设计 token、独立的资源编译、Hook 集成点。读完能复制 themes/aurora 改新主题上线。

把 InnoCMS 装上跑起来后,企业官网的第二步永远是「让网站长得像我们自己」。默认主题是演示用的,不是生产用的。这篇文章把"从零开始做一个企业官网主题"的完整流程拆开讲:目录结构、Blade 继承、SCSS 设计 token、独立的资源编译、Hook 集成点。读完你能复制 themes/aurora/ 改一个新主题出来上线。

如果你只是想深度理解已有主题而不是从零写,建议先看 《Aurora 主题深度解析》。本文聚焦"动手做一个"而不是"分析一个"。

主题到底是什么

InnoCMS 的主题不是"皮肤",而是前台所有可见部分的完整接管。一个主题包含:

  • HTML 结构(Blade 模板)
  • 样式(SCSS 编译成 CSS)
  • 脚本(JS 文件)
  • 静态资源(图片、字体、favicon)
  • 主题级翻译文件(覆盖 innopacks/front 的默认文案)
  • 演示数据(首次安装时 seed 进数据库)

切换主题 = 改一个配置项 + 重编译资源,整个站点的视觉和行为完全变化。这就是为什么 InnoCMS 不需要"主题切换插件"——主题本身就是一等公民。

目录结构

InnoCMS 主题的标准目录结构

所有主题放在 themes/<code>/ 下,目录名就是主题 code(小写、kebab-case)。标准结构:

themes/your-brand/
├── config.json              # 主题元信息(必填)
├── views/                   # Blade 模板
│   ├── layouts/
│   │   └── app.blade.php    # 根布局(HTML/head/nav/footer)
│   ├── home.blade.php       # 首页
│   ├── articles/
│   │   ├── index.blade.php  # 文章列表
│   │   └── show.blade.php   # 文章详情
│   ├── catalogs/
│   ├── pages/
│   ├── tags/
│   └── errors/
├── assets/
│   ├── scss/                # SCSS 源码
│   │   ├── abstracts/       # 变量、mixin、function
│   │   ├── base/            # reset、排版
│   │   ├── layout/          # 网格、容器
│   │   ├── sections/        # header、footer、nav
│   │   ├── pages/           # 各页面专属样式
│   │   └── utilities/       # 工具类
│   ├── js/                  # 前端脚本
│   └── images/              # 主题静态图片
├── public/                  # 编译输出(gitignore)
├── lang/                    # 主题级翻译
└── demo/                    # 演示数据

config.json:主题的身份证

{
  "code": "your-brand",
  "name": "Your Brand Theme",
  "version": "1.0.0",
  "author": "Your Team",
  "description": "Enterprise corporate site theme",
  "thumbnail": "thumbnail.png"
}

这个文件决定后台「主题管理」列表里怎么显示。code 必须跟目录名完全一致,否则加载时报错。

Blade 继承:三层结构

所有具体页面(home / articles / pages)都继承自 layouts/app.blade.php。根布局提供整页骨架:

<!-- themes/your-brand/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="{{ locale_code() }}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ $page_title ?? setting('system_name') }}</title>
  @yield('meta')
  <link rel="stylesheet" href="{{ theme_asset('css/app.css') }}">
  @hookinsert('layouts.header.assets')
</head>
<body>
  @include('layouts.partials.header')
  @yield('content')
  @include('layouts.partials.footer')
  @hookinsert('layouts.footer.assets')
</body>
</html>

具体页面只填 @section('content')

<!-- themes/your-brand/views/articles/show.blade.php -->
@extends('layouts.app')

@section('meta')
  <meta name="description" content="{{ $article->translation->meta_description }}">
@endsection

@section('content')
  <article class="article-detail">
    <h1>{{ $article->translation->title }}</h1>
    {!! $article->translation->content !!}
  </article>
@endsection

关键技巧:在根布局里预留 @hookinsert('layouts.header.assets')@hookinsert('layouts.footer.assets'),让插件可以注入自己的 CSS/JS,而不需要改主题文件。

SCSS 设计 Token:可维护性的根基

所有视觉变量集中在 assets/scss/abstracts/_variables.scss,用 CSS 自定义属性暴露给运行时:

// assets/scss/abstracts/_variables.scss
:root {
  --brand-primary: #0EA5E9;        // 品牌主色
  --brand-cta:     #F97316;        // CTA 强调色
  --text-heading:  #082F49;
  --text-body:     #475569;
  --bg:            #FFFFFF;
  --bg-alt:        #F8FAFC;
  --border:        #E2E8F0;
  --radius-lg:     20px;
  --shadow:        0 4px 12px rgba(12,74,110,.06);
  --transition:    .25s cubic-bezier(.4,0,.2,1);
}

组件 SCSS 引用这些变量而不是硬编码颜色。改 --brand-primary 一个值,全站按钮、链接、高亮、icon 全跟着变。这是「换主题色只需要一行」的实现基础。

资源编译:THEME 环境变量是关键

InnoCMS 主题资源编译流水线

这是新主题作者踩过最隐蔽的坑。npm run prod 编译的是 InnoCMS 默认 front 资源,不会编译主题目录里的 SCSS。主题编译必须带 THEME 环境变量:

THEME=your-brand npm run build

这条命令扫描 themes/your-brand/assets/,把 SCSS 编译成 CSS 输出到 themes/your-brand/public/css/app.css。Blade 模板通过 theme_asset('css/app.css') 引用,自动带 ?v={timestamp} 缓存破坏。

常见踩坑:改完 SCSS 发现页面没变化,第一步永远是 ls -la themes/your-brand/public/css/app.css 看 mtime 是不是刚刚 —— HTTP 200 完全不能说明 CSS 更新了。

主题级翻译

每个主题可以有自己的翻译文件,覆盖 innopacks/front 的默认文案。结构:

themes/your-brand/lang/
  └── zh-cn/
      └── front.php
// themes/your-brand/lang/zh-cn/front.php
return [
  'read_more'    => '阅读全文',
  'subscribe'    => '订阅更新',
  'contact_sales' => '联系销售',
];

Blade 里用 {{ theme_trans('front.read_more') }} 调用。如果主题没有这条 key,自动 fallback 到 innopacks/front 的默认翻译。

演示数据:第一次安装的种子

主题的 demo/ 目录可以放演示用的文章、单页、设置数据。后台「主题管理」点「导入演示数据」时,系统会把这些内容 seed 到数据库,让用户开箱看到一个"丰满"的演示站。

themes/your-brand/demo/
  ├── articles.json       # 文章列表
  ├── pages.json          # 单页列表
  ├── settings.json       # 主题设置覆盖
  └── images/             # 演示图片

最佳实践:演示数据用 picsum.photos 占位图(https://picsum.photos/seed/<unique>/1400/900),不要打包真实图片,让主题包保持小体积。

从零到上线的完整流程

  1. 复制 themes/aurora/ 改名 themes/your-brand/
  2. config.json 的 code/name/version/author
  3. assets/scss/abstracts/_variables.scss 里的 --brand-primary--brand-cta 两个值
  4. views/layouts/app.blade.php 里的 logo 和 footer 文案
  5. THEME=your-brand npm run build 编译资源
  6. 后台「主题管理」激活 your-brand

这套流程从开始到企业官网可访问,平均 4 小时 —— 这就是 InnoCMS 主题系统设计的回报。后续文章会拆 hook 系统和插件开发,把"二次开发"这条路彻底打通。