How to scaffold a new theme, build and pack it, publish to pureadmin.io, and understand the integrity system that protects the ecosystem.
The fastest way to create a new theme is to download the template and tools from pureadmin.io:
# Scaffold a new theme project (downloads template + tools)
npx @keenmate/pureadmin init my-theme "My Theme"
cd my-theme
# Install dependencies, build, and pack
npm install
npm run build
npm run pack
Or manually via the API:
curl -o template.zip "https://pureadmin.io/api/tools/template?id=my-theme&name=My%20Theme"
unzip template.zip -d my-theme && cd my-theme
This gives you a ready-to-go theme project with:
theme.json — theme manifest with schema reference and starter colorssrc/scss/my-theme.scss
— SCSS source importing Pure Admin core with variable overrides
package.json — npm scripts for build, pack, and publishmy-theme/\n├── theme.json # Theme manifest (required)\n├── src/scss/my-theme.scss # SCSS source (required)\n├── dist/my-theme.css # Compiled CSS (generated)\n├── pure-admin.json # CLI config (server URL, API key)\n├── assets/ # Optional: fonts, images, icons\n│ ├── fonts/\n│ └── logo.svg\n├── preview/ # Optional: thumbnail screenshots\n│ └── thumbnail.png\n└── package.json
The theme manifest describes your theme's metadata, color variants, modes, and exports. It follows the Pure Admin theme schema.
{
"$schema": "https://pureadmin.io/schemas/pure-admin-theme.schema.json",
"manifestVersion": "1.0",
"name": "My Theme",
"id": "my-theme",
"version": "1.0.0",
"description": "A custom Pure Admin theme.",
"author": "Your Name",
"license": "MIT",
"coreVersion": ">=2.0.0",
"colorVariants": [
{
"id": "",
"name": "Default",
"file": "dist/my-theme.css",
"modes": [
{
"id": "light",
"name": "Light",
"default": true,
"colors": {
"accent": "#3b82f6",
"background": "#f9fafb",
"surface": "#ffffff",
"text": "#1a1a1a"
}
}
]
}
],
"exports": {
"css": "./dist/my-theme.css",
"scss": "./src/scss/my-theme.scss"
},
"external_domains": [],
"scripts": []
}
Key fields:
manifestVersion — schema version, currently "1.0"id — lowercase slug with hyphens, used in filenames and URLscolorVariants
— array of color variants, each with modes (light/dark) and per-mode color palettes
external_domains
— any domains your CSS references via url()
(font CDNs, image hosts)
scripts
— any JavaScript files in the package (must be declared or upload is rejected)
Compile your SCSS to CSS:
# Build current theme\nnpx @keenmate/pureadmin build\n\n# In a multi-theme workspace, build specific themes\nnpx @keenmate/pureadmin build audi corporate
This runs sass
with the correct load paths to resolve @keenmate/pure-admin-core
imports.
The output lands in dist/my-theme.css.
Package your theme into a distributable ZIP:
# Build + pack\nnpx @keenmate/pureadmin pack\n\n# Pack without rebuilding\nnpx @keenmate/pureadmin pack --no-build\n\n# Pack specific themes in a workspace\nnpx @keenmate/pureadmin pack audi corporate
The pack tool:
theme.jsonurl() paths in CSS/SCSS to be ZIP-relativecontent_shapure-admin-theme-my-theme-1.0.0.zipUpload your theme to pureadmin.io:
# Build + pack + upload (all in one)\nnpx @keenmate/pureadmin publish\n\n# Publish specific theme with explicit API key\nnpx @keenmate/pureadmin publish audi --api-key your-key-here\n\n# Publish to a different server\nnpx @keenmate/pureadmin publish --server https://staging.pureadmin.io
Store your API key and server URL in pure-admin.json (project-level)
or ~/.pure-admin.json (user-level) so you don't have to pass them every time:
// pure-admin.json
{
"url": "https://pureadmin.io",
"apiKey": "your-key-here"
}
On upload, the server validates your package:
url()
references must be declared in external_domains
.js
files must be listed in scripts
If the theme already exists with identical checksums, the server responds with
"unchanged"
and skips the update.
Every theme package includes cryptographic integrity data. This enables change detection, HTTP caching, and trust verification.
// Inside the ZIP's theme.json after packing:
"checksums": {
"files": {
"css/my-theme.css": "sha256:abcd...",
"scss/my-theme.scss": "sha256:ef01...",
"assets/fonts/MyFont.woff2": "sha256:2345...",
"README.md": "sha256:6789..."
},
"metadata": "sha256:abab...",
"content_sha": "sha256:cdcd..."
}
checksums.files
— SHA-256 of every file in the ZIP (excluding theme.json itself)
checksums.metadata
— SHA-256 of canonical JSON of descriptive fields (name, description, version, etc.). Detects content-only updates without CSS changes.
checksums.content_sha
— combined hash of all file checksums + metadata hash. This is the single value that represents the entire package state.
The content_sha
is used as the HTTP ETag
header when you fetch a theme from the API.
Clients can verify downloads by comparing:
# 1. Get the ETag from the API
ETAG=$(curl -sI https://pureadmin.io/api/themes/my-theme | grep -i etag | awk '{print $2}' | tr -d '"\r')
# 2. Download the ZIP and extract content_sha
curl -o my-theme.zip https://pureadmin.io/api/themes/my-theme/download
SHA=$(unzip -p my-theme.zip theme.json | python3 -c "import sys,json; print(json.load(sys.stdin)['checksums']['content_sha'])")
# 3. Compare — they should match
[ "$ETAG" = "$SHA" ] && echo 'Integrity verified' || echo 'MISMATCH'
All theme operations — building, packing, publishing, downloading — are handled
by the @keenmate/pureadmin CLI. No separate scripts needed.
# Install globally\nnpm install -g @keenmate/pureadmin\n\n# Or use via npx (no install)\nnpx @keenmate/pureadmin --help
Key commands for theme authors:
| Command | Description |
|---|---|
| pureadmin build | Compile SCSS to CSS |
| pureadmin pack | Build + package into ZIP with integrity checksums |
| pureadmin publish | Build + pack + upload to pureadmin.io |
| pureadmin init <id> | Scaffold a new theme project |
See the npm package for full documentation.