@koehler8/cms 1.0.0-beta.9 → 1.1.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.
- package/CHANGELOG.md +1522 -0
- package/README.md +108 -28
- package/bin/cms-create-extension.js +7 -0
- package/bin/cms-create-site.js +7 -0
- package/bin/cms-create-theme.js +7 -0
- package/bin/cms-ssg-build.js +13 -0
- package/bin/cms-validate-extensions.js +4 -0
- package/bin/cms-validate-themes.js +11 -0
- package/extensions/manifest.schema.json +1 -1
- package/package.json +33 -17
- package/scripts/create-extension.mjs +11 -0
- package/scripts/create-site.mjs +11 -0
- package/scripts/create-theme.mjs +11 -0
- package/scripts/generate-public-assets.js +70 -36
- package/scripts/image-variants/cache.js +40 -0
- package/scripts/image-variants/classifier.js +53 -0
- package/scripts/image-variants/config.js +54 -0
- package/scripts/image-variants/index.js +18 -0
- package/scripts/image-variants/manifest.js +76 -0
- package/scripts/image-variants/planner.js +95 -0
- package/scripts/image-variants/reconcile.js +194 -0
- package/scripts/image-variants/renderer.js +51 -0
- package/scripts/lib/extension-manifests.mjs +70 -0
- package/scripts/scaffold.mjs +242 -0
- package/scripts/ssg-build/findEmptyRenders.mjs +42 -0
- package/scripts/ssg-build/index.mjs +253 -0
- package/scripts/validate-extensions.mjs +50 -382
- package/scripts/validate-themes.mjs +37 -5
- package/src/components/About.vue +4 -2
- package/src/components/AboutValue.vue +5 -4
- package/src/components/ComingSoon.vue +12 -1
- package/src/components/ComingSoonModal.vue +33 -61
- package/src/components/Contact.vue +216 -120
- package/src/components/DraftGate.vue +211 -0
- package/src/components/Footer.vue +15 -2
- package/src/components/FooterMinimal.vue +13 -12
- package/src/components/Header.vue +365 -10
- package/src/components/Hero.vue +17 -8
- package/src/components/Home.vue +82 -66
- package/src/components/Intro.vue +4 -2
- package/src/components/IntroGate.vue +30 -73
- package/src/components/NotFound.vue +100 -0
- package/src/components/Plan.vue +10 -4
- package/src/components/Portfolio.vue +29 -3
- package/src/components/Principles.vue +3 -1
- package/src/components/Spacer.vue +23 -0
- package/src/components/Spacer15.vue +3 -4
- package/src/components/Spacer30.vue +3 -4
- package/src/components/Spacer40.vue +3 -4
- package/src/components/Spacer60.vue +3 -4
- package/src/components/StickyCTA.vue +23 -1
- package/src/components/Team.vue +3 -1
- package/src/composables/useComponentResolver.js +49 -1
- package/src/composables/useDraftGate.js +93 -0
- package/src/composables/useFocusTrap.js +85 -0
- package/src/composables/usePageConfig.js +136 -36
- package/src/composables/usePageMeta.js +178 -5
- package/src/composables/usePromoBackgroundStyles.js +30 -14
- package/src/extensions/extensionLoader.js +34 -24
- package/src/main.js +69 -6
- package/src/router/index.js +98 -37
- package/src/styles/base.css +11 -2
- package/src/themes/buildCssVarMap.js +34 -0
- package/src/utils/appInfo.js +8 -5
- package/src/utils/breadcrumbs.js +121 -0
- package/src/utils/canonicalUrl.js +53 -0
- package/src/utils/componentRegistry.js +98 -16
- package/src/utils/cookieConsent.js +38 -24
- package/src/utils/draftMode.js +65 -0
- package/src/utils/imageSources.js +9 -1
- package/src/utils/jsonLd.js +66 -0
- package/src/utils/loadConfig.js +106 -6
- package/src/utils/notFound.js +52 -0
- package/src/utils/robotsGenerator.js +95 -0
- package/src/utils/sha256.js +20 -0
- package/src/utils/sitemapGenerator.js +119 -0
- package/src/utils/socialMeta.js +100 -0
- package/src/utils/ssgRoutes.js +40 -0
- package/src/utils/trackingContext.js +16 -22
- package/src/utils/webAppManifest.js +85 -0
- package/src/utils/webStorage.js +82 -0
- package/templates/index.html +17 -24
- package/templates/scaffolds/extension/README.md +53 -0
- package/templates/scaffolds/extension/extension.config.json +10 -0
- package/templates/scaffolds/extension/index.js +37 -0
- package/templates/scaffolds/extension/package.json +22 -0
- package/templates/scaffolds/site/.env.example +7 -0
- package/templates/scaffolds/site/.nvmrc +1 -0
- package/templates/scaffolds/site/CLAUDE.md +121 -0
- package/templates/scaffolds/site/README.md +25 -0
- package/templates/scaffolds/site/amplify.yml +22 -0
- package/templates/scaffolds/site/package.json +26 -0
- package/templates/scaffolds/site/site/assets/img/.gitkeep +0 -0
- package/templates/scaffolds/site/site/components/.gitkeep +0 -0
- package/templates/scaffolds/site/site/content/content.config.json +3 -0
- package/templates/scaffolds/site/site/content/en/pages/home.json +8 -0
- package/templates/scaffolds/site/site/content/en/shared.json +5 -0
- package/templates/scaffolds/site/site/content/en/site.json +10 -0
- package/templates/scaffolds/site/site/style.css +4 -0
- package/templates/scaffolds/site/vite.config.js +15 -0
- package/templates/scaffolds/theme/README.md +50 -0
- package/templates/scaffolds/theme/index.js +8 -0
- package/templates/scaffolds/theme/package.json +21 -0
- package/templates/scaffolds/theme/theme.config.js +228 -0
- package/templates/scaffolds/theme/theme.css +14 -0
- package/themes/base/theme.config.js +100 -57
- package/vite-plugin.js +549 -32
- package/public/robots.txt +0 -5
- package/scripts/check-overflow.mjs +0 -33
- package/scripts/migrate-to-content-dirs.mjs +0 -223
- package/scripts/patch-lru-cache-tla.js +0 -164
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cms-ext-__SLUG__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "__DISPLAY_NAME__ extension for @koehler8/cms.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js",
|
|
10
|
+
"./config": "./extension.config.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.js",
|
|
14
|
+
"extension.config.json",
|
|
15
|
+
"components",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@koehler8/cms": "^1.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Site token used by some extensions and the provisioning tooling (safe to
|
|
2
|
+
# leave as-is for a plain content site).
|
|
3
|
+
VITE_TOKEN=__SLUG__
|
|
4
|
+
|
|
5
|
+
# Optional: override the framework version string reported in the client
|
|
6
|
+
# bundle (defaults to the installed @koehler8/cms package version).
|
|
7
|
+
# VITE_APP_VERSION=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20.19.0
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# site-__SLUG__
|
|
2
|
+
|
|
3
|
+
(staging URL TBD)
|
|
4
|
+
|
|
5
|
+
A site built on the [`@koehler8/cms`](https://github.com/koehler8/cms) framework. **Owner: TBD.** This file is auto-loaded by Claude Code in this directory — read it before making changes.
|
|
6
|
+
|
|
7
|
+
## Project status
|
|
8
|
+
|
|
9
|
+
Freshly scaffolded via `npx cms-create-site`. Home page renders the bundled `Header` + `Hero` + `Footer` components with placeholder copy. No theme, extension, or custom components yet — see "Where things go" below for the path each new addition should take.
|
|
10
|
+
|
|
11
|
+
## Working with the site owner
|
|
12
|
+
|
|
13
|
+
Assume the owner is not a developer unless you know otherwise. Default behaviors:
|
|
14
|
+
|
|
15
|
+
- Explain trade-offs in plain language; propose options, don't present decisions.
|
|
16
|
+
- Confirm before structural or visual changes that will be hard to reverse.
|
|
17
|
+
- Push small, reviewable changes frequently and verify on the live URL.
|
|
18
|
+
- Don't over-engineer — most requests can be satisfied by editing `pages/*.json` + bundled CMS components. Reach for a custom Vue component only when the bundled set clearly can't do the job.
|
|
19
|
+
|
|
20
|
+
## Where things go (the navigation guide)
|
|
21
|
+
|
|
22
|
+
| Goal | Where |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Edit page copy / hero text / section content | `site/content/{locale}/pages/{pageId}.json` under `content.{configKey}.*` keys |
|
|
25
|
+
| Edit site title, theme key, support email | `site/content/{locale}/site.json` |
|
|
26
|
+
| Edit header logo text, nav items, footer flags | `site/content/{locale}/shared.json` |
|
|
27
|
+
| Add a translation | Mirror the keys you want to override into `site/content/{otherLocale}/...` (only set the keys that differ — the rest inherit from the base locale) |
|
|
28
|
+
| Add an asset (image, icon) | `site/assets/img/...`. Reference it from a component via `resolveAsset('img/foo.png')` or a URL string in content (`"img/foo.png"`) |
|
|
29
|
+
| Override the favicon | Drop a real `.ico` at `site/favicon.ico`; the `cms-generate-public-assets` script picks it up |
|
|
30
|
+
| Add a one-off Vue component (this site only) | `site/components/Foo.vue`. Auto-globbed by the CMS — reference it as `"Foo"` in any page's `components[]`. Read content via `inject('pageContent')`, use `<style scoped>` with `var(--brand-*)` tokens. |
|
|
31
|
+
| Add a component intended to be reused on other sites | The local extension at `extensions/<slug>/`. Add `components/Foo.vue`, register it in `extension.config.json`, and add a dynamic import to `index.js`. Eventually publishable as `@koehler8/cms-ext-<slug>`. |
|
|
32
|
+
| Customize colors, typography, surfaces | `themes/<slug>/theme.config.js`. The CMS regenerates `virtual:cms-theme-vars.css` from the manifest on next build; CMS components consume the vars via `var(--brand-*)`. |
|
|
33
|
+
| Activate a theme | Set `"theme": "<slug>"` in `site/content/{baseLocale}/site.json`. |
|
|
34
|
+
| Add a new page | Create `site/content/{locale}/pages/{pageId}.json` with a `path` and `components[]` array. vite-ssg pre-renders one HTML file per page automatically. |
|
|
35
|
+
| Add a new locale | Create `site/content/{locale}/...` mirroring the base locale's structure. Auto-discovered. |
|
|
36
|
+
| Add a new theme to the project | `npx cms-create-theme <slug>` (scaffolds `themes/<slug>/`, then follow the printed next-steps). |
|
|
37
|
+
| Add a new local extension | `npx cms-create-extension <slug>`. |
|
|
38
|
+
|
|
39
|
+
If you find yourself writing a component that conceptually belongs in a "regions" or "agents" or "podcasts" sister site, **promote it from `site/components/` into `extensions/<slug>/`** so it can eventually become a published shared package.
|
|
40
|
+
|
|
41
|
+
## Stack at a glance
|
|
42
|
+
|
|
43
|
+
- `@koehler8/cms` ^1.0.0 — Vue 3 + vite-ssg framework
|
|
44
|
+
- `@koehler8/cms-ext-compliance` — cookie banner / legal pages (registered by default)
|
|
45
|
+
- `[site/components/](site/components/)` — auto-globbed Vue files for one-off custom UI specific to this site. **The default home for new components.**
|
|
46
|
+
- `[themes/](themes/)`, `[extensions/](extensions/)` — currently empty. Add via the `cms-create-{theme,extension}` CLIs above.
|
|
47
|
+
|
|
48
|
+
## File layout
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
site/content/{locale}/ per-locale content (auto-discovered)
|
|
52
|
+
site.json site title, description, theme key, support email
|
|
53
|
+
shared.json cross-page settings — header logo / nav, footer flags
|
|
54
|
+
pages/{pageId}.json per-page components[] + content.{configKey}.* keys
|
|
55
|
+
pages/home.json home page (the only page in a freshly scaffolded site)
|
|
56
|
+
site/components/ auto-globbed Vue files (the default home for one-off
|
|
57
|
+
custom UI). Reference by basename in components[].
|
|
58
|
+
site/assets/img/ site-specific images / icons
|
|
59
|
+
site/style.css non-critical site overrides (loaded async by the CMS;
|
|
60
|
+
fine for things that wouldn't cause a visible flash)
|
|
61
|
+
site/favicon.ico optional override; `cms-generate-public-assets`
|
|
62
|
+
picks it up over the auto-generated letter-favicon
|
|
63
|
+
themes/<slug>/ site-local CMS theme — destined to lift into a
|
|
64
|
+
standalone @koehler8/cms-theme-<slug> repo
|
|
65
|
+
extensions/<slug>/ site-local CMS extension — destined to lift into a
|
|
66
|
+
standalone @koehler8/cms-ext-<slug> repo
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Common tasks (quick recipes)
|
|
70
|
+
|
|
71
|
+
### Edit hero / footer copy
|
|
72
|
+
Open `site/content/{baseLocale}/pages/home.json`. Hero text lives under `content.promo.*`; footer text under `content.footer.*` (or whatever configKey the components use). Push and the deploy pipeline picks it up.
|
|
73
|
+
|
|
74
|
+
### Edit nav links
|
|
75
|
+
`site/content/{baseLocale}/shared.json` → `content.header.navItems[N]`. Each entry is `{ text, href, target? }`. Add `"target": "_blank"` for off-site links.
|
|
76
|
+
|
|
77
|
+
### Add a custom component
|
|
78
|
+
1. Create `site/components/Foo.vue`.
|
|
79
|
+
2. `inject('pageContent')` for content; `<style scoped>` with `var(--brand-*)` tokens.
|
|
80
|
+
3. Reference as `"Foo"` in any page's `components[]`.
|
|
81
|
+
|
|
82
|
+
### Add a new page
|
|
83
|
+
1. Create `site/content/{baseLocale}/pages/{pageId}.json` with a `path` (e.g. `/about`) and `components[]`.
|
|
84
|
+
2. Add a `navItems` entry pointing at the new path in `shared.json` if it should be in the nav.
|
|
85
|
+
3. Push — vite-ssg generates `dist/{path}/index.html` automatically.
|
|
86
|
+
|
|
87
|
+
### Change colors / typography
|
|
88
|
+
Edit `themes/<slug>/theme.config.js`. The CMS regenerates `virtual:cms-theme-vars.css` from the manifest on next build; CMS components and your custom components consume the vars via `var(--brand-*)`, so the change cascades.
|
|
89
|
+
|
|
90
|
+
## Lockfile and npm version — read before bumping any dep
|
|
91
|
+
|
|
92
|
+
This project pins **Node 20.19 / npm 10.8** to match AWS Amplify exactly. Local installs with a different npm version produce subtly different lockfiles that look fine locally but break CI.
|
|
93
|
+
|
|
94
|
+
Two failure modes seen on sister sites:
|
|
95
|
+
1. `Cannot find module '@rolldown/binding-linux-x64-gnu'` — npm 11 regen stripped optional-dep entries Amplify needs.
|
|
96
|
+
2. `npm ci ... Missing: @types/react@..., @noble/hashes@... from lock file` — npm 11 omitted entries npm 10 expects.
|
|
97
|
+
|
|
98
|
+
Rules:
|
|
99
|
+
- **Always run `nvm use` before `npm install`**. `.nvmrc` pins 20.19.0 (which ships npm 10.8.x).
|
|
100
|
+
- **Prefer targeted bumps**: `npm install <pkg>@<ver> --no-audit --no-fund`. **Never** `rm package-lock.json && npm install` unless you're on the pinned npm version.
|
|
101
|
+
- **The build uses `npm install`**, not `npm ci` (see `amplify.yml`) for drift tolerance.
|
|
102
|
+
|
|
103
|
+
## Local dev
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
nvm use
|
|
107
|
+
cp .env.example .env # placeholder values; not used by default
|
|
108
|
+
npm install
|
|
109
|
+
npm run dev # Vite dev server on http://localhost:5173
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Build:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm run generate:public-assets # generates favicon/og-image from site.json
|
|
116
|
+
npm run build:ssg # pre-renders dist/{locale}/{page}/index.html
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Deploy
|
|
120
|
+
|
|
121
|
+
Pushes to `main` trigger an automatic AWS Amplify build per `amplify.yml`. Set up the Amplify app + custom domain via the AWS console (or `aws amplify create-app` + `create-domain-association`) — see the existing sister sites' setup notes if available, or the koehler8 site repos as reference.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# site-__SLUG__
|
|
2
|
+
|
|
3
|
+
A site built on [`@koehler8/cms`](https://github.com/koehler8/cms).
|
|
4
|
+
|
|
5
|
+
For architecture, conventions, and a navigation guide for "where things go" (content, components, themes, extensions), read **[CLAUDE.md](CLAUDE.md)**.
|
|
6
|
+
|
|
7
|
+
## Local development
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
nvm use # Node 20.19.0 per .nvmrc
|
|
11
|
+
cp .env.example .env
|
|
12
|
+
npm install
|
|
13
|
+
npm run dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Build
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run generate:public-assets
|
|
20
|
+
npm run build:ssg
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Deployment
|
|
24
|
+
|
|
25
|
+
Pushes to `main` trigger an automatic AWS Amplify build (see [amplify.yml](amplify.yml)).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
frontend:
|
|
3
|
+
phases:
|
|
4
|
+
preBuild:
|
|
5
|
+
commands:
|
|
6
|
+
- nvm install 20.19.0 && nvm use 20.19.0
|
|
7
|
+
# Use `npm install` (not `npm ci`) so the build tolerates
|
|
8
|
+
# lockfile/npm-version drift. See CLAUDE.md → "Lockfile and
|
|
9
|
+
# npm version" for the rationale.
|
|
10
|
+
- npm install --prefer-offline --no-audit --no-fund
|
|
11
|
+
- npm run generate:public-assets
|
|
12
|
+
build:
|
|
13
|
+
commands:
|
|
14
|
+
- npm run build:ssg
|
|
15
|
+
artifacts:
|
|
16
|
+
baseDirectory: dist
|
|
17
|
+
files:
|
|
18
|
+
- '**/*'
|
|
19
|
+
cache:
|
|
20
|
+
paths:
|
|
21
|
+
- node_modules/**/*
|
|
22
|
+
- .npm/**/*
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@koehler8/site-__SLUG__",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"build:ssg": "cms-ssg-build",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"generate:public-assets": "cms-generate-public-assets --site-dir ./site"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@koehler8/cms": "^1.0.0",
|
|
15
|
+
"@koehler8/cms-ext-compliance": "^1.0.0",
|
|
16
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
17
|
+
"vite": "^8.0.3",
|
|
18
|
+
"vite-ssg": "^28.2.2",
|
|
19
|
+
"vue": "^3.5.22",
|
|
20
|
+
"vue-router": "^5.0.4"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.19.0",
|
|
24
|
+
"npm": "^10.8.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import cms from '@koehler8/cms/vite';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
plugins: cms({
|
|
5
|
+
siteDir: './site',
|
|
6
|
+
// Add local themes (created via `npx cms-create-theme <slug>`) here:
|
|
7
|
+
// themes: ['cms-theme-<slug>'],
|
|
8
|
+
// Then activate by setting `"theme": "<slug>"` in site.json.
|
|
9
|
+
extensions: [
|
|
10
|
+
'@koehler8/cms-ext-compliance',
|
|
11
|
+
// Add local extensions (created via `npx cms-create-extension <slug>`) here:
|
|
12
|
+
// 'cms-ext-<slug>',
|
|
13
|
+
],
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# cms-theme-__SLUG__
|
|
2
|
+
|
|
3
|
+
__DISPLAY_NAME__ theme for [`@koehler8/cms`](https://github.com/koehler8/cms).
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Starter scaffold. Edit [theme.config.js](theme.config.js) to define your palette, typography, surfaces, etc. The bland slate-and-sand defaults are placeholders.
|
|
8
|
+
|
|
9
|
+
## Activate
|
|
10
|
+
|
|
11
|
+
After scaffolding (`npx cms-create-theme __SLUG__`), wire it into your site:
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
// package.json
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"cms-theme-__SLUG__": "file:./themes/__SLUG__"
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// vite.config.js
|
|
22
|
+
plugins: cms({
|
|
23
|
+
siteDir: './site',
|
|
24
|
+
themes: ['cms-theme-__SLUG__'],
|
|
25
|
+
}),
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```jsonc
|
|
29
|
+
// site/content/{baseLocale}/site.json
|
|
30
|
+
{
|
|
31
|
+
"theme": "__SLUG__"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run `npm install`, then build/preview to see your theme applied.
|
|
36
|
+
|
|
37
|
+
## Edit
|
|
38
|
+
|
|
39
|
+
All theming happens in [theme.config.js](theme.config.js). The CMS turns each `tokens.*` block into `--brand-*` / `--theme-*` / `--ui-*` CSS variables under `:root[data-site-theme="__SLUG__"]`, bundled into a sync stylesheet so they apply during the initial paint.
|
|
40
|
+
|
|
41
|
+
[theme.css](theme.css) is for supplemental rules the manifest doesn't cover (font `@import`s, brand-color aliases, etc.).
|
|
42
|
+
|
|
43
|
+
## Extracting to a standalone repo
|
|
44
|
+
|
|
45
|
+
Once the theme stabilises:
|
|
46
|
+
|
|
47
|
+
1. Copy `themes/__SLUG__/` to a new repo named `cms-theme-__SLUG__/`
|
|
48
|
+
2. Rename the `name` in `package.json` to `@koehler8/cms-theme-__SLUG__` and add the standard repo metadata (homepage, bugs, repository) following the pattern of `@koehler8/cms-theme-neon`
|
|
49
|
+
3. Publish to GitHub Packages / npm
|
|
50
|
+
4. Replace the `file:` dependency in the consuming site with the published version
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import manifest from './theme.config.js';
|
|
2
|
+
// Side-effect import (not `?inline`): @koehler8/cms >= 1.0.0-beta.8 bundles
|
|
3
|
+
// theme.css as a sync <link rel="stylesheet">, applied during the initial
|
|
4
|
+
// paint instead of injected via JS after Vue hydrates.
|
|
5
|
+
import './theme.css';
|
|
6
|
+
|
|
7
|
+
export default { manifest };
|
|
8
|
+
export { manifest };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cms-theme-__SLUG__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "__DISPLAY_NAME__ theme for @koehler8/cms.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./config": "./theme.config.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"theme.config.js",
|
|
14
|
+
"theme.css",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@koehler8/cms": "^1.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// __DISPLAY_NAME__ theme manifest. Defines the design tokens the CMS turns
|
|
2
|
+
// into `--brand-*`, `--theme-*`, and `--ui-*` CSS variables under
|
|
3
|
+
// `:root[data-site-theme="__SLUG__"]`. CMS components consume these via
|
|
4
|
+
// `var(--brand-*)`, so editing the values here cascades automatically.
|
|
5
|
+
//
|
|
6
|
+
// All token blocks below are required by the validator. The default palette
|
|
7
|
+
// is intentionally bland (cool slate + warm sand accent) so it's obvious
|
|
8
|
+
// what to change first. See @koehler8/cms-theme-neon, -frog, -aurora,
|
|
9
|
+
// -southpark, -swamp for production-quality references.
|
|
10
|
+
|
|
11
|
+
const slate900 = '#0f172a';
|
|
12
|
+
const slate800 = '#1e293b';
|
|
13
|
+
const slate700 = '#334155';
|
|
14
|
+
const slate200 = '#e2e8f0';
|
|
15
|
+
const slate100 = '#f1f5f9';
|
|
16
|
+
const sand = '#f4a261';
|
|
17
|
+
const sandDeep = '#d97e3a';
|
|
18
|
+
const ink = '#0b1220';
|
|
19
|
+
const cream = '#f7f3ec';
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
slug: '__SLUG__',
|
|
23
|
+
meta: {
|
|
24
|
+
name: '__DISPLAY_NAME__',
|
|
25
|
+
version: '0.1.0',
|
|
26
|
+
},
|
|
27
|
+
tokens: {
|
|
28
|
+
palette: {
|
|
29
|
+
primary: slate800,
|
|
30
|
+
primaryAccent: slate900,
|
|
31
|
+
secondary: slate700,
|
|
32
|
+
accent: sand,
|
|
33
|
+
accentSoft: 'rgba(244, 162, 97, 0.18)',
|
|
34
|
+
neutral: ink,
|
|
35
|
+
neutralStrong: cream,
|
|
36
|
+
neutralSoft: slate200,
|
|
37
|
+
inverse: '#ffffff',
|
|
38
|
+
success: '#2a9d8f',
|
|
39
|
+
info: slate700,
|
|
40
|
+
warning: sand,
|
|
41
|
+
critical: '#d62828',
|
|
42
|
+
criticalSoft: 'rgba(214, 40, 40, 0.18)',
|
|
43
|
+
},
|
|
44
|
+
text: {
|
|
45
|
+
primary: cream,
|
|
46
|
+
muted: slate200,
|
|
47
|
+
mutedStrong: slate100,
|
|
48
|
+
inverse: ink,
|
|
49
|
+
accent: sand,
|
|
50
|
+
onAccent: '#ffffff',
|
|
51
|
+
},
|
|
52
|
+
surfaces: {
|
|
53
|
+
base: ink,
|
|
54
|
+
baseAlt: slate900,
|
|
55
|
+
raised: slate800,
|
|
56
|
+
sunken: '#070b14',
|
|
57
|
+
callout: 'rgba(244, 162, 97, 0.12)',
|
|
58
|
+
card: 'rgba(30, 41, 59, 0.92)',
|
|
59
|
+
cardAlt: 'rgba(51, 65, 85, 0.92)',
|
|
60
|
+
overlay: 'rgba(11, 18, 32, 0.96)',
|
|
61
|
+
backdrop: 'rgba(11, 18, 32, 0.78)',
|
|
62
|
+
border: 'rgba(226, 232, 240, 0.18)',
|
|
63
|
+
divider: 'rgba(226, 232, 240, 0.12)',
|
|
64
|
+
input: 'rgba(30, 41, 59, 0.96)',
|
|
65
|
+
chip: 'rgba(244, 162, 97, 0.22)',
|
|
66
|
+
chipAccent: sand,
|
|
67
|
+
helper: {
|
|
68
|
+
background: 'rgba(226, 232, 240, 0.08)',
|
|
69
|
+
hover: 'rgba(226, 232, 240, 0.16)',
|
|
70
|
+
text: cream,
|
|
71
|
+
border: 'rgba(226, 232, 240, 0.2)',
|
|
72
|
+
heading: cream,
|
|
73
|
+
body: 'rgba(247, 243, 236, 0.85)',
|
|
74
|
+
},
|
|
75
|
+
tabs: {
|
|
76
|
+
background: 'rgba(30, 41, 59, 0.92)',
|
|
77
|
+
border: 'rgba(226, 232, 240, 0.16)',
|
|
78
|
+
shadow: '0 18px 34px rgba(11, 18, 32, 0.45)',
|
|
79
|
+
tabColor: 'rgba(247, 243, 236, 0.88)',
|
|
80
|
+
activeBackground: 'rgba(244, 162, 97, 0.18)',
|
|
81
|
+
activeColor: '#ffffff',
|
|
82
|
+
activeShadow: '0 18px 40px rgba(11, 18, 32, 0.55), inset 0 1px 0 rgba(255,255,255,0.16)',
|
|
83
|
+
stepBackground: 'rgba(255, 255, 255, 0.06)',
|
|
84
|
+
stepBorder: 'rgba(255, 255, 255, 0.18)',
|
|
85
|
+
stepColor: 'rgba(255, 255, 255, 0.72)',
|
|
86
|
+
activeStepBackground: 'rgba(244, 162, 97, 0.24)',
|
|
87
|
+
activeStepBorder: 'rgba(244, 162, 97, 0.5)',
|
|
88
|
+
activeStepColor: sand,
|
|
89
|
+
},
|
|
90
|
+
field: {
|
|
91
|
+
background: 'rgba(30, 41, 59, 0.96)',
|
|
92
|
+
border: 'rgba(226, 232, 240, 0.16)',
|
|
93
|
+
shadow: '0 8px 24px rgba(11, 18, 32, 0.32)',
|
|
94
|
+
addonBackground: 'rgba(11, 18, 32, 0.92)',
|
|
95
|
+
addonBorder: 'rgba(226, 232, 240, 0.16)',
|
|
96
|
+
addonColor: cream,
|
|
97
|
+
inputColor: cream,
|
|
98
|
+
inputPlaceholder: 'rgba(241, 245, 249, 0.55)',
|
|
99
|
+
},
|
|
100
|
+
strip: {
|
|
101
|
+
background: 'rgba(11, 18, 32, 0.9)',
|
|
102
|
+
border: 'rgba(226, 232, 240, 0.16)',
|
|
103
|
+
text: cream,
|
|
104
|
+
},
|
|
105
|
+
chrome: {
|
|
106
|
+
background: 'rgba(11, 18, 32, 0.94)',
|
|
107
|
+
text: cream,
|
|
108
|
+
shadow: '0 18px 45px rgba(2, 5, 12, 0.55)',
|
|
109
|
+
compactShadow: '0 12px 32px rgba(2, 5, 12, 0.65)',
|
|
110
|
+
},
|
|
111
|
+
backdropPrimary: {
|
|
112
|
+
background: 'linear-gradient(150deg, rgba(11,18,32,0.98), rgba(15,23,42,0.92))',
|
|
113
|
+
before: 'radial-gradient(circle, rgba(226,232,240,0.3), rgba(0,0,0,0))',
|
|
114
|
+
after: 'radial-gradient(circle, rgba(244,162,97,0.5), rgba(0,0,0,0))',
|
|
115
|
+
},
|
|
116
|
+
backdropSecondary: {
|
|
117
|
+
background: 'linear-gradient(140deg, rgba(11,18,32,0.97), rgba(15,23,42,0.92))',
|
|
118
|
+
before: 'radial-gradient(circle, rgba(244,162,97,0.45), rgba(0,0,0,0))',
|
|
119
|
+
after: 'radial-gradient(circle, rgba(226,232,240,0.4), rgba(0,0,0,0))',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
typography: {
|
|
123
|
+
bodyFamily: '"Inter", "Helvetica Neue", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
|
|
124
|
+
headingFamily: '"Inter", "Helvetica Neue", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
|
|
125
|
+
monoFamily: '"JetBrains Mono", "SF Mono", Menlo, Consolas, monospace',
|
|
126
|
+
baseSize: '16px',
|
|
127
|
+
scale: 1.125,
|
|
128
|
+
weightRegular: 400,
|
|
129
|
+
weightMedium: 500,
|
|
130
|
+
weightBold: 600,
|
|
131
|
+
letterSpacingTight: '-0.015em',
|
|
132
|
+
letterSpacingWide: '0.08em',
|
|
133
|
+
},
|
|
134
|
+
ctas: {
|
|
135
|
+
primary: {
|
|
136
|
+
bg: `linear-gradient(135deg, ${sand} 0%, ${sandDeep} 100%)`,
|
|
137
|
+
text: '#ffffff',
|
|
138
|
+
border: '1px solid rgba(255,255,255,0.18)',
|
|
139
|
+
hoverBg: `linear-gradient(135deg, ${sandDeep} 0%, ${sand} 100%)`,
|
|
140
|
+
hoverBorder: '1px solid rgba(255,255,255,0.24)',
|
|
141
|
+
shadow: '0 18px 35px rgba(11, 18, 32, 0.45)',
|
|
142
|
+
},
|
|
143
|
+
secondary: {
|
|
144
|
+
bg: 'rgba(30, 41, 59, 0.92)',
|
|
145
|
+
text: cream,
|
|
146
|
+
border: 'rgba(226, 232, 240, 0.2)',
|
|
147
|
+
hoverBg: 'rgba(226, 232, 240, 0.08)',
|
|
148
|
+
hoverBorder: 'rgba(226, 232, 240, 0.32)',
|
|
149
|
+
shadow: '0 8px 24px rgba(11, 18, 32, 0.32)',
|
|
150
|
+
},
|
|
151
|
+
ghost: {
|
|
152
|
+
bg: 'transparent',
|
|
153
|
+
text: cream,
|
|
154
|
+
border: 'rgba(226, 232, 240, 0.2)',
|
|
155
|
+
hoverBg: 'rgba(226, 232, 240, 0.08)',
|
|
156
|
+
hoverBorder: 'rgba(226, 232, 240, 0.32)',
|
|
157
|
+
shadow: 'none',
|
|
158
|
+
},
|
|
159
|
+
link: {
|
|
160
|
+
text: sand,
|
|
161
|
+
hoverText: sandDeep,
|
|
162
|
+
underline: 'rgba(244, 162, 97, 0.45)',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
chips: {
|
|
166
|
+
neutral: {
|
|
167
|
+
bg: 'rgba(226, 232, 240, 0.12)',
|
|
168
|
+
text: cream,
|
|
169
|
+
border: 'rgba(226, 232, 240, 0.2)',
|
|
170
|
+
},
|
|
171
|
+
accent: {
|
|
172
|
+
bg: 'rgba(244, 162, 97, 0.22)',
|
|
173
|
+
text: '#ffffff',
|
|
174
|
+
border: 'rgba(244, 162, 97, 0.45)',
|
|
175
|
+
},
|
|
176
|
+
outline: {
|
|
177
|
+
bg: 'transparent',
|
|
178
|
+
text: sand,
|
|
179
|
+
border: 'rgba(244, 162, 97, 0.45)',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
focus: {
|
|
183
|
+
ring: '0 0 0 2px rgba(226, 232, 240, 0.45)',
|
|
184
|
+
ringMuted: '0 0 0 1px rgba(226, 232, 240, 0.32)',
|
|
185
|
+
outline: 'rgba(226, 232, 240, 0.65)',
|
|
186
|
+
shadowInset: '0 0 22px rgba(226, 232, 240, 0.32)',
|
|
187
|
+
},
|
|
188
|
+
radii: {
|
|
189
|
+
sm: '6px',
|
|
190
|
+
md: '12px',
|
|
191
|
+
lg: '20px',
|
|
192
|
+
pill: '999px',
|
|
193
|
+
full: '50%',
|
|
194
|
+
},
|
|
195
|
+
elevation: {
|
|
196
|
+
flat: '0 8px 24px rgba(11, 18, 32, 0.18)',
|
|
197
|
+
raised: '0 18px 38px rgba(11, 18, 32, 0.32)',
|
|
198
|
+
overlay: '0 24px 56px rgba(11, 18, 32, 0.5)',
|
|
199
|
+
},
|
|
200
|
+
utility: {
|
|
201
|
+
divider: 'rgba(226, 232, 240, 0.16)',
|
|
202
|
+
inputBorder: 'rgba(226, 232, 240, 0.16)',
|
|
203
|
+
inputText: cream,
|
|
204
|
+
inputPlaceholder: 'rgba(241, 245, 249, 0.55)',
|
|
205
|
+
selectionBg: 'rgba(244, 162, 97, 0.32)',
|
|
206
|
+
selectionText: '#ffffff',
|
|
207
|
+
gradientHero: 'linear-gradient(150deg, rgba(11,18,32,0.98), rgba(15,23,42,0.92))',
|
|
208
|
+
gradientPromo: 'linear-gradient(140deg, rgba(11,18,32,0.97), rgba(15,23,42,0.92))',
|
|
209
|
+
bodyBackground: [
|
|
210
|
+
'linear-gradient(155deg, rgba(11,18,32,0.98), rgba(15,23,42,0.92))',
|
|
211
|
+
],
|
|
212
|
+
modalSurface: 'rgba(30, 41, 59, 0.96)',
|
|
213
|
+
modalBorder: 'rgba(226, 232, 240, 0.16)',
|
|
214
|
+
modalShadow: '0 24px 56px rgba(11, 18, 32, 0.5)',
|
|
215
|
+
modalRadius: '20px',
|
|
216
|
+
chartTrack: 'rgba(30, 41, 59, 0.85)',
|
|
217
|
+
chartCenterText: cream,
|
|
218
|
+
statusHeadline: {
|
|
219
|
+
background: 'linear-gradient(120deg, rgba(226,232,240,0.12), rgba(244,162,97,0.18))',
|
|
220
|
+
color: sand,
|
|
221
|
+
shadow: '0 12px 24px rgba(11, 18, 32, 0.32), inset 0 1px 0 rgba(255,255,255,0.18)',
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
assets: {
|
|
226
|
+
css: './theme.css',
|
|
227
|
+
},
|
|
228
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* __DISPLAY_NAME__ theme — supplemental rules and brand-specific aliases.
|
|
2
|
+
*
|
|
3
|
+
* Most theming happens via the tokens object in theme.config.js — the CMS
|
|
4
|
+
* derives `:root[data-site-theme="__SLUG__"] { --brand-*, --theme-* }` from
|
|
5
|
+
* the manifest and bundles it into `virtual:cms-theme-vars.css` (loaded as
|
|
6
|
+
* a sync <link rel="stylesheet">). This file is for things the manifest
|
|
7
|
+
* doesn't cover: font @imports, brand-color aliases, transitions, etc.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
:root[data-site-theme="__SLUG__"] {
|
|
11
|
+
/* Brand color aliases (also exposed by the manifest, mirrored here for
|
|
12
|
+
parity with the existing @koehler8/cms-theme-* packages). */
|
|
13
|
+
/* --brand-foo: #XXXXXX; */
|
|
14
|
+
}
|