ultimate-jekyll-manager 1.4.3 → 1.6.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 +36 -0
- package/CLAUDE-ATTRIBUTION.md +215 -0
- package/CLAUDE.md +7 -6
- package/README.md +1 -0
- package/dist/assets/css/pages/test/libraries/layers/index.scss +28 -0
- package/dist/assets/js/modules/redirect.js +5 -4
- package/dist/assets/js/pages/download/index.js +1 -1
- package/dist/assets/js/pages/feedback/index.js +1 -1
- package/dist/assets/js/pages/test/libraries/layers/index.js +11 -0
- package/dist/assets/themes/_template/README.md +50 -0
- package/dist/assets/themes/_template/_config.scss +60 -0
- package/dist/assets/themes/_template/_theme.js +13 -4
- package/dist/assets/themes/_template/_theme.scss +16 -4
- package/dist/assets/themes/_template/css/base/_root.scss +19 -0
- package/dist/assets/themes/_template/css/components/_components.scss +23 -0
- package/dist/assets/themes/classy/README.md +18 -6
- package/dist/assets/themes/neobrutalism/README.md +98 -0
- package/dist/assets/themes/neobrutalism/_config.scss +139 -0
- package/dist/assets/themes/neobrutalism/_theme.js +27 -0
- package/dist/assets/themes/neobrutalism/_theme.scss +33 -0
- package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
- package/dist/assets/themes/neobrutalism/css/base/_root.scss +80 -0
- package/dist/assets/themes/neobrutalism/css/base/_typography.scss +77 -0
- package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +25 -0
- package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
- package/dist/assets/themes/neobrutalism/css/components/_cards.scss +69 -0
- package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
- package/dist/assets/themes/neobrutalism/css/components/_infinite-scroll.scss +94 -0
- package/dist/assets/themes/neobrutalism/css/layout/_general.scss +200 -0
- package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +153 -0
- package/dist/assets/themes/neobrutalism/js/initialize-tooltips.js +20 -0
- package/dist/assets/themes/neobrutalism/js/navbar-scroll.js +29 -0
- package/dist/assets/themes/neobrutalism/pages/index.scss +227 -0
- package/dist/assets/themes/neobrutalism/pages/pricing/index.scss +267 -0
- package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.js +9 -0
- package/dist/assets/themes/neobrutalism/pages/test/libraries/layers/index.scss +7 -0
- package/dist/build.js +2 -5
- package/dist/commands/install.js +1 -1
- package/dist/commands/setup.js +41 -0
- package/dist/defaults/CLAUDE.md +5 -1
- package/dist/defaults/dist/_includes/core/head.html +17 -0
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +4 -4
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +2 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/feedback.html +7 -3
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/core/base.html +31 -0
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/index.html +345 -0
- package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/pricing.html +483 -0
- package/dist/defaults/dist/pages/test/libraries/layers.html +57 -0
- package/dist/defaults/src/_config.yml +2 -0
- package/dist/defaults/test/_init.js +10 -0
- package/dist/gulp/tasks/defaults.js +8 -0
- package/dist/gulp/tasks/sass.js +43 -2
- package/dist/gulp/tasks/translation.js +11 -0
- package/dist/gulp/tasks/utils/manage-test-layers.js +97 -0
- package/dist/index.js +30 -4
- package/dist/test/runner.js +62 -0
- package/dist/test/suites/build/manager.test.js +11 -4
- package/dist/test/suites/build/mode-helpers.test.js +54 -2
- package/dist/utils/mode-helpers.js +65 -40
- package/docs/assets.md +6 -1
- package/docs/environment-detection.md +85 -0
- package/docs/test-framework.md +48 -3
- package/docs/themes.md +451 -0
- package/package.json +2 -1
- package/docs/cross-context-helpers.md +0 -75
- package/package copy.json +0 -75
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// /test — Theme (neobrutalism) page CSS — the #theme layer.
|
|
2
|
+
// Loads AFTER the universal layer and turns the "css-theme" dot green.
|
|
3
|
+
// Compiles standalone, so no theme tokens are needed here — just the proof.
|
|
4
|
+
|
|
5
|
+
.layer-dot[data-layer="css-theme"] {
|
|
6
|
+
background: #30a46c; // green
|
|
7
|
+
}
|
package/dist/build.js
CHANGED
|
@@ -110,11 +110,8 @@ Manager.actLikeProduction = function () {
|
|
|
110
110
|
}
|
|
111
111
|
Manager.prototype.actLikeProduction = Manager.actLikeProduction;
|
|
112
112
|
|
|
113
|
-
// getEnvironment
|
|
114
|
-
|
|
115
|
-
return Manager.isServer() ? 'production' : 'development';
|
|
116
|
-
}
|
|
117
|
-
Manager.prototype.getEnvironment = Manager.getEnvironment;
|
|
113
|
+
// getEnvironment() is the SSOT and lives in src/utils/mode-helpers.js (alongside the is*()
|
|
114
|
+
// family). It's mixed onto the Manager via the attachTo() call below, same as in EM/BXM.
|
|
118
115
|
|
|
119
116
|
// getConfig: requires and parses config.yml
|
|
120
117
|
Manager.getConfig = function (type) {
|
package/dist/commands/install.js
CHANGED
package/dist/commands/setup.js
CHANGED
|
@@ -39,6 +39,7 @@ module.exports = async function (options) {
|
|
|
39
39
|
options.checkLocality = options.checkLocality !== 'false';
|
|
40
40
|
options.publishGitHubToken = options.publishGitHubToken !== 'false';
|
|
41
41
|
options.deduplicatePosts = options.deduplicatePosts !== 'false';
|
|
42
|
+
options.removeLegacyTeamMembers = options.removeLegacyTeamMembers !== 'false';
|
|
42
43
|
options.migrate = options.migrate !== 'false';
|
|
43
44
|
|
|
44
45
|
// Quick mode: skip slow/network operations
|
|
@@ -137,6 +138,11 @@ module.exports = async function (options) {
|
|
|
137
138
|
if (options.deduplicatePosts) {
|
|
138
139
|
await deduplicatePosts();
|
|
139
140
|
}
|
|
141
|
+
|
|
142
|
+
// Remove legacy default team members (first-name-only format, e.g. team/alex)
|
|
143
|
+
if (options.removeLegacyTeamMembers) {
|
|
144
|
+
await removeLegacyTeamMembers();
|
|
145
|
+
}
|
|
140
146
|
};
|
|
141
147
|
|
|
142
148
|
// --- Version check functions ---
|
|
@@ -527,6 +533,41 @@ async function deduplicatePosts() {
|
|
|
527
533
|
}
|
|
528
534
|
}
|
|
529
535
|
|
|
536
|
+
async function removeLegacyTeamMembers() {
|
|
537
|
+
// Legacy default team members that used the first-name-only format.
|
|
538
|
+
// These shipped before team members were renamed to first-last (e.g. team/alex -> team/alex-raeburn).
|
|
539
|
+
const legacyTeamMembers = ['alex'];
|
|
540
|
+
|
|
541
|
+
logger.log('Checking for legacy default team members to remove...');
|
|
542
|
+
|
|
543
|
+
let removedCount = 0;
|
|
544
|
+
|
|
545
|
+
legacyTeamMembers.forEach((slug) => {
|
|
546
|
+
// The _team collection file (any markdown/html extension)
|
|
547
|
+
const memberFiles = glob(`src/_team/${slug}.{md,markdown,html}`, { nodir: true });
|
|
548
|
+
|
|
549
|
+
memberFiles.forEach((filePath) => {
|
|
550
|
+
jetpack.remove(filePath);
|
|
551
|
+
logger.log(` ✓ Removed legacy team member: ${filePath}`);
|
|
552
|
+
removedCount++;
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// The associated image directory (src/assets/images/team/<slug>)
|
|
556
|
+
const imageDir = path.join(process.cwd(), 'src', 'assets', 'images', 'team', slug);
|
|
557
|
+
|
|
558
|
+
if (jetpack.exists(imageDir)) {
|
|
559
|
+
jetpack.remove(imageDir);
|
|
560
|
+
logger.log(` ✓ Removed legacy team member image directory: team/${slug}`);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
if (removedCount > 0) {
|
|
565
|
+
logger.log(logger.format.green(`✓ Removed ${removedCount} legacy team member(s)`));
|
|
566
|
+
} else {
|
|
567
|
+
logger.log('No legacy team members found');
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
530
571
|
// --- Migration functions ---
|
|
531
572
|
|
|
532
573
|
async function migrate() {
|
package/dist/defaults/CLAUDE.md
CHANGED
|
@@ -29,8 +29,12 @@ npm run build # production build (UJ_BUILD_MODE=true): clean → setup →
|
|
|
29
29
|
npm run deploy # build → `npu sync --message='Deploy'` (publishes _site/)
|
|
30
30
|
npx mgr test # run framework + project test suites (build / page / boot layers)
|
|
31
31
|
npx mgr audit # HTML validation + spellcheck + optional Lighthouse
|
|
32
|
+
npx mgr install dev # use LOCAL ultimate-jekyll-manager source (to test framework edits)
|
|
33
|
+
npx mgr install live # restore the published ultimate-jekyll-manager from npm
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
> Editing the UJM framework source while working here? Run `npx mgr install dev` so this project picks up your uncommitted framework changes (it otherwise uses its installed `node_modules/ultimate-jekyll-manager`). Run `npx mgr install live` to switch back.
|
|
37
|
+
|
|
34
38
|
## Where things live
|
|
35
39
|
|
|
36
40
|
- `src/_config.yml` — Jekyll config: brand, theme, meta, web_manager (Firebase). `Manager.getConfig('project')` reads this. **`brand.id` + `theme.id` are required.**
|
|
@@ -72,7 +76,7 @@ At build time, `require('ultimate-jekyll-manager/build')` exposes:
|
|
|
72
76
|
- `Manager.getConfig(type)` — read `_config.yml` (`'project'` or `'main'`)
|
|
73
77
|
- `Manager.getPackage(type)` — read `package.json` (`'project'` or `'main'`)
|
|
74
78
|
- `Manager.getUJMConfig()` — read `config/ultimate-jekyll-manager.json`
|
|
75
|
-
- `Manager.getEnvironment()` — `'development'
|
|
79
|
+
- `Manager.getEnvironment()` — `'development' | 'testing' | 'production'` (mutually exclusive; testing wins). Gate side effects on the intentional check (`isProduction()` for prod-only; `isDevelopment() || isTesting()` for local-or-test) — never `!isDevelopment()`.
|
|
76
80
|
- `Manager.isBuildMode()` / `isQuickMode()` / `isServer()` / `actLikeProduction()` — env-gated flags
|
|
77
81
|
- `Manager.logger(name)` — timestamped logger instance
|
|
78
82
|
- `Manager.require(path)` — escape hatch for UJM transitive deps (use sparingly)
|
|
@@ -26,12 +26,15 @@
|
|
|
26
26
|
|
|
27
27
|
{% iftruthy page.resolved.asset_path %}
|
|
28
28
|
{% capture page-css-path %}/assets/css/pages/{{ page.resolved.asset_path }}.bundle.css{% endcapture %}
|
|
29
|
+
{% capture page-css-theme-path %}/assets/css/pages/{{ page.resolved.asset_path }}.{{ page.resolved.theme.id }}.bundle.css{% endcapture %}
|
|
29
30
|
{% endiftruthy %}
|
|
30
31
|
{% iffalsy page.resolved.asset_path %}
|
|
31
32
|
{% if page.canonical.path == "/" %}
|
|
32
33
|
{% capture page-css-path %}/assets/css/pages/index.bundle.css{% endcapture %}
|
|
34
|
+
{% capture page-css-theme-path %}/assets/css/pages/index.{{ page.resolved.theme.id }}.bundle.css{% endcapture %}
|
|
33
35
|
{% else %}
|
|
34
36
|
{% capture page-css-path %}/assets/css/pages{{ page.canonical.path }}/index.bundle.css{% endcapture %}
|
|
37
|
+
{% capture page-css-theme-path %}/assets/css/pages{{ page.canonical.path }}/index.{{ page.resolved.theme.id }}.bundle.css{% endcapture %}
|
|
35
38
|
{% endif %}
|
|
36
39
|
{% endiffalsy %}
|
|
37
40
|
|
|
@@ -210,6 +213,20 @@
|
|
|
210
213
|
{% endif %}
|
|
211
214
|
{% endiffile %}
|
|
212
215
|
|
|
216
|
+
<!-- Then, the active theme's page-specific bundle (loaded AFTER base so it can override).
|
|
217
|
+
Only present when the theme ships page CSS for this path — otherwise nothing loads
|
|
218
|
+
and the theme's component/general styles handle the page. No fallback needed. -->
|
|
219
|
+
{% iffile page-css-theme-path %}
|
|
220
|
+
<link rel="stylesheet" type="text/css" href="{{ site.url }}{{ page-css-theme-path }}?cb={{ site.uj.cache_breaker }}"/>
|
|
221
|
+
|
|
222
|
+
<!-- Dev log -->
|
|
223
|
+
{% if jekyll.environment == "development" %}
|
|
224
|
+
<script>
|
|
225
|
+
console.info("Theme page-specific css loading: #main{{ page-css-theme-path }}");
|
|
226
|
+
</script>
|
|
227
|
+
{% endif %}
|
|
228
|
+
{% endiffile %}
|
|
229
|
+
|
|
213
230
|
<!-- Style - Scripts are Disabled -->
|
|
214
231
|
<noscript>
|
|
215
232
|
{{ page-ie-script }}
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
</div>
|
|
71
71
|
|
|
72
72
|
<!-- Bottom Section -->
|
|
73
|
-
<div class="row mt-4 pt-3 border-top">
|
|
73
|
+
<div class="row mt-4 pt-3 border-top align-items-center">
|
|
74
74
|
<div class="col-md-6 d-flex align-items-center">
|
|
75
75
|
<!-- Language Dropdown -->
|
|
76
|
-
<div class="d-
|
|
76
|
+
<div class="d-flex align-items-center me-3">
|
|
77
77
|
<div class="dropup uj-language-dropdown">
|
|
78
78
|
<button class="btn btn-sm btn-outline-adaptive dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
79
79
|
<i class="fa fa-sm me-1">
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
</button>
|
|
86
86
|
<ul class="dropdown-menu">
|
|
87
87
|
{% assign default_language = site.translation.default | default: "en" %}
|
|
88
|
-
{% if site.translation.languages.size > 0 %}
|
|
88
|
+
{% if site.translation.enabled and site.translation.languages.size > 0 %}
|
|
89
89
|
{% assign all_languages = site.translation.languages | push: default_language %}
|
|
90
90
|
{% else %}
|
|
91
91
|
{% assign all_languages = "" | split: "" | push: default_language %}
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
|
|
106
106
|
<!-- Social Links -->
|
|
107
107
|
{% if data.socials.enabled %}
|
|
108
|
-
<div class="d-
|
|
108
|
+
<div class="d-flex align-items-center">
|
|
109
109
|
{% if data.socials.list and data.socials.list.size > 0 %}
|
|
110
110
|
{% assign social_list = data.socials.list %}
|
|
111
111
|
{% else %}
|
|
@@ -330,6 +330,7 @@ cta:
|
|
|
330
330
|
<a
|
|
331
331
|
href="{{ config_url }}"
|
|
332
332
|
class="btn btn-primary btn-lg d-flex align-items-center justify-content-center"
|
|
333
|
+
data-download="true"
|
|
333
334
|
>
|
|
334
335
|
{% uj_icon type.icon, "fa-3xl me-2" %}
|
|
335
336
|
{{ type.name }}
|
|
@@ -339,6 +340,7 @@ cta:
|
|
|
339
340
|
<button
|
|
340
341
|
type="button"
|
|
341
342
|
class="btn btn-primary btn-lg d-flex align-items-center justify-content-center"
|
|
343
|
+
data-download="true"
|
|
342
344
|
>
|
|
343
345
|
{% uj_icon type.icon, "fa-3xl me-2" %}
|
|
344
346
|
{{ type.name }}
|
|
@@ -167,9 +167,13 @@ prerender_icons:
|
|
|
167
167
|
<img src="https://cdn.itwcreativeworks.com/assets/general/images/feedback/love.png?cb={{ site.uj.cache_breaker }}" alt="Thank you" width="80" height="80">
|
|
168
168
|
</div>
|
|
169
169
|
<h3 class="mb-3" id="review-modal-label">You're awesome!</h3>
|
|
170
|
-
<p class="text-muted mb-
|
|
171
|
-
We're so glad you love {{ site.brand.name }}!
|
|
170
|
+
<p class="text-muted mb-3">
|
|
171
|
+
We're so glad you love {{ site.brand.name }}! Share your experience on a review site to be entered to win a $100 gift card.
|
|
172
172
|
</p>
|
|
173
|
+
<div class="alert alert-warning text-start small mb-4" role="alert">
|
|
174
|
+
{% uj_icon "circle-exclamation", "me-1" %}
|
|
175
|
+
You must actually post your review to be eligible for the $100 gift card — closing this without publishing a review won't qualify.
|
|
176
|
+
</div>
|
|
173
177
|
|
|
174
178
|
<!-- Copy-paste feedback -->
|
|
175
179
|
<div class="text-start mb-4">
|
|
@@ -188,7 +192,7 @@ prerender_icons:
|
|
|
188
192
|
|
|
189
193
|
<a id="review-modal-link" href="#" target="_blank" rel="noopener noreferrer" class="btn btn-primary btn-lg mb-3">
|
|
190
194
|
{% uj_icon "arrow-up-right-from-square", "me-2" %}
|
|
191
|
-
|
|
195
|
+
Post your review
|
|
192
196
|
</a>
|
|
193
197
|
<div>
|
|
194
198
|
<button type="button" class="btn btn-link text-muted" data-bs-dismiss="modal">Maybe later</button>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: core/root
|
|
4
|
+
|
|
5
|
+
### THEME CONFIG ###
|
|
6
|
+
theme:
|
|
7
|
+
# html:
|
|
8
|
+
# class: ""
|
|
9
|
+
head:
|
|
10
|
+
content: |
|
|
11
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;600;800;900&family=Space+Grotesk:wght@400;500;700&family=Space+Mono:wght@400;700&display=swap" media="print" onload="this.media='all'">
|
|
12
|
+
body:
|
|
13
|
+
class: "d-flex flex-column min-vh-100"
|
|
14
|
+
# main:
|
|
15
|
+
# class: "flex-fill"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<!-- Nav (controlled by page/layout front matter) -->
|
|
19
|
+
{% unless page.resolved.theme.nav.enabled == false %}
|
|
20
|
+
{%- include /frontend/sections/nav.html -%}
|
|
21
|
+
{% endunless %}
|
|
22
|
+
|
|
23
|
+
<!-- Main Content -->
|
|
24
|
+
<main id="main-content" class="flex-fill {{ page.resolved.theme.main.class | uj_liquify }}" aria-label="Main content">
|
|
25
|
+
{{ content | uj_content_format }}
|
|
26
|
+
</main>
|
|
27
|
+
|
|
28
|
+
<!-- Footer (controlled by page/layout front matter) -->
|
|
29
|
+
{% unless page.resolved.theme.footer.enabled == false %}
|
|
30
|
+
{%- include /frontend/sections/footer.html -%}
|
|
31
|
+
{% endunless %}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
---
|
|
2
|
+
### ALL PAGES ###
|
|
3
|
+
layout: themes/[ site.theme.id ]/frontend/core/base
|
|
4
|
+
|
|
5
|
+
#### THEME CONFIG ####
|
|
6
|
+
theme:
|
|
7
|
+
main:
|
|
8
|
+
class: "pt-0"
|
|
9
|
+
|
|
10
|
+
### PAGE CONFIG ###
|
|
11
|
+
# Hero Section
|
|
12
|
+
hero:
|
|
13
|
+
tagline: "Introducing {{ site.brand.name }}"
|
|
14
|
+
headline: "The #1 platform for business"
|
|
15
|
+
headline_accent: "success"
|
|
16
|
+
description: "AI automation for modern businesses made simple"
|
|
17
|
+
primary_button:
|
|
18
|
+
text: "Get started free"
|
|
19
|
+
icon: "rocket"
|
|
20
|
+
href: "/dashboard"
|
|
21
|
+
secondary_button:
|
|
22
|
+
text: "See pricing"
|
|
23
|
+
icon: "book-open"
|
|
24
|
+
href: "/pricing"
|
|
25
|
+
|
|
26
|
+
# Trusted By Section
|
|
27
|
+
trusted_by:
|
|
28
|
+
headline: "Trusted by the best"
|
|
29
|
+
logos:
|
|
30
|
+
- name: "amazon"
|
|
31
|
+
- name: "google"
|
|
32
|
+
- name: "hubspot"
|
|
33
|
+
- name: "paypal"
|
|
34
|
+
- name: "slack"
|
|
35
|
+
- name: "shopify"
|
|
36
|
+
- name: "stripe"
|
|
37
|
+
|
|
38
|
+
# Showcase Section
|
|
39
|
+
showcase:
|
|
40
|
+
superheadline:
|
|
41
|
+
icon: "cubes"
|
|
42
|
+
text: "Showcase"
|
|
43
|
+
headline: "What makes {{ site.brand.name }}"
|
|
44
|
+
headline_accent: "different"
|
|
45
|
+
subheadline: "Powerful features designed to accelerate your success"
|
|
46
|
+
items:
|
|
47
|
+
- title: "Seamless integration"
|
|
48
|
+
description: "Connect with your existing tools and platforms effortlessly. Our system automatically syncs data and streamlines your workflow for maximum efficiency."
|
|
49
|
+
- title: "Data-driven insights"
|
|
50
|
+
description: "Make informed decisions with real-time analytics and actionable insights that help you understand what matters most to your business."
|
|
51
|
+
- title: "Smart automation"
|
|
52
|
+
description: "Focus on what matters while our intelligent platform handles routine tasks automatically, working 24/7 to keep your business running smoothly."
|
|
53
|
+
|
|
54
|
+
# Features Section
|
|
55
|
+
features:
|
|
56
|
+
superheadline:
|
|
57
|
+
icon: "sparkles"
|
|
58
|
+
text: "How it works"
|
|
59
|
+
headline: "Design made"
|
|
60
|
+
headline_accent: "simple"
|
|
61
|
+
subheadline: "Get unlimited design requests delivered fast with our streamlined process."
|
|
62
|
+
cta_button:
|
|
63
|
+
text: "Get started"
|
|
64
|
+
icon: "rocket"
|
|
65
|
+
href: "/dashboard"
|
|
66
|
+
items:
|
|
67
|
+
- title: "Subscribe"
|
|
68
|
+
description: "Choose your plan and submit unlimited design requests."
|
|
69
|
+
icon: "rotate"
|
|
70
|
+
- title: "Receive"
|
|
71
|
+
description: "Get your designs delivered in as fast as two business days."
|
|
72
|
+
icon: "bolt"
|
|
73
|
+
- title: "Revise"
|
|
74
|
+
description: "Request unlimited revisions until it's perfect."
|
|
75
|
+
icon: "thumbs-up"
|
|
76
|
+
|
|
77
|
+
# Testimonials Section
|
|
78
|
+
testimonials:
|
|
79
|
+
superheadline:
|
|
80
|
+
icon: "megaphone"
|
|
81
|
+
text: "Testimonials"
|
|
82
|
+
headline: "People {% uj_icon \"heart\", \"text-danger\" %} "
|
|
83
|
+
headline_accent: "{{ site.brand.name }}"
|
|
84
|
+
subheadline: "Hear from real people who have transformed their business with us"
|
|
85
|
+
items:
|
|
86
|
+
- quote: "This platform transformed my business. The support is incredible!"
|
|
87
|
+
author: "Sarah Johnson"
|
|
88
|
+
role: "CEO"
|
|
89
|
+
company: "TechStart Inc"
|
|
90
|
+
initial: "S"
|
|
91
|
+
- quote: "Best investment I've made. Achieved more here than with any consultant."
|
|
92
|
+
author: "Michael Chen"
|
|
93
|
+
role: "Founder"
|
|
94
|
+
company: "DataFlow"
|
|
95
|
+
initial: "M"
|
|
96
|
+
- quote: "The community support made all the difference in our growth journey."
|
|
97
|
+
author: "Emily Davis"
|
|
98
|
+
role: "Director"
|
|
99
|
+
company: "Creative Studio"
|
|
100
|
+
initial: "E"
|
|
101
|
+
|
|
102
|
+
# Stats Section
|
|
103
|
+
stats:
|
|
104
|
+
- number: "50,000+"
|
|
105
|
+
label: "Active users"
|
|
106
|
+
sublabel: "From 120+ countries"
|
|
107
|
+
color: "blue"
|
|
108
|
+
- number: "200+"
|
|
109
|
+
label: "Solutions"
|
|
110
|
+
sublabel: "Industry-leading"
|
|
111
|
+
color: "pink"
|
|
112
|
+
- number: "4.9"
|
|
113
|
+
label: "Average rating"
|
|
114
|
+
color: "yellow"
|
|
115
|
+
- number: "98%"
|
|
116
|
+
label: "Success rate"
|
|
117
|
+
sublabel: "Business growth"
|
|
118
|
+
color: "green"
|
|
119
|
+
|
|
120
|
+
# CTA Section
|
|
121
|
+
cta:
|
|
122
|
+
superheadline:
|
|
123
|
+
icon: "rocket"
|
|
124
|
+
text: "Launch"
|
|
125
|
+
headline: "Ready to transform your"
|
|
126
|
+
headline_accent: "business?"
|
|
127
|
+
description: "Join thousands of successful companies who have already accelerated their growth with {{ site.brand.name }}"
|
|
128
|
+
primary_button:
|
|
129
|
+
text: "Start today"
|
|
130
|
+
icon: "rocket"
|
|
131
|
+
href: "/dashboard"
|
|
132
|
+
secondary_button:
|
|
133
|
+
text: "Talk to an expert"
|
|
134
|
+
icon: "comments"
|
|
135
|
+
href: "/contact"
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
{% comment %}
|
|
139
|
+
NEOBRUTALISM HOMEPAGE
|
|
140
|
+
Reuses the same page.resolved.* data contract as the classy homepage, but with
|
|
141
|
+
a deliberately different STRUCTURE: an asymmetric split hero, an ink-framed logo
|
|
142
|
+
marquee, alternating offset showcase blocks, big numbered step blocks, oversized
|
|
143
|
+
color-block stats, and a full-bleed CTA. Color blocks rotate through the nb accent
|
|
144
|
+
palette. See docs/themes.md "Per-page HTML layout overrides".
|
|
145
|
+
{% endcomment %}
|
|
146
|
+
|
|
147
|
+
<!-- ============================================ -->
|
|
148
|
+
<!-- HERO — asymmetric split: oversized headline | offset action stack -->
|
|
149
|
+
<!-- ============================================ -->
|
|
150
|
+
<section class="section-hero text-bg-warning">
|
|
151
|
+
<div class="container">
|
|
152
|
+
<div class="row align-items-center g-5">
|
|
153
|
+
<!-- Left: kicker + giant headline + description -->
|
|
154
|
+
<div class="col-lg-7">
|
|
155
|
+
<p class="text-uppercase fw-semibold mb-4">{{ page.resolved.hero.tagline }}</p>
|
|
156
|
+
<h1 class="hero-title mb-4">
|
|
157
|
+
{{ page.resolved.hero.headline }}
|
|
158
|
+
<span class="text-accent">{{ page.resolved.hero.headline_accent }}</span>
|
|
159
|
+
</h1>
|
|
160
|
+
<p class="fs-3 fw-medium mb-0" style="max-width: 36ch;">
|
|
161
|
+
{{ page.resolved.hero.description }}
|
|
162
|
+
</p>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Right: stacked, full-width CTA buttons (d-grid = full-width children) -->
|
|
166
|
+
<div class="col-lg-5">
|
|
167
|
+
<div class="d-grid gap-3 hero-actions">
|
|
168
|
+
<a href="{{ page.resolved.hero.primary_button.href }}" class="btn btn-dark btn-lg">
|
|
169
|
+
{% uj_icon page.resolved.hero.primary_button.icon, "me-2" %}{{ page.resolved.hero.primary_button.text }}
|
|
170
|
+
</a>
|
|
171
|
+
<a href="{{ page.resolved.hero.secondary_button.href }}" class="btn btn-light btn-lg">
|
|
172
|
+
{% uj_icon page.resolved.hero.secondary_button.icon, "me-2" %}{{ page.resolved.hero.secondary_button.text }}
|
|
173
|
+
</a>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</section>
|
|
179
|
+
|
|
180
|
+
<!-- ============================================ -->
|
|
181
|
+
<!-- TRUSTED BY — ink-framed marquee strip -->
|
|
182
|
+
<!-- ============================================ -->
|
|
183
|
+
<section class="logo-strip">
|
|
184
|
+
<div class="container">
|
|
185
|
+
<div class="logo-strip-box">
|
|
186
|
+
<span class="logo-strip-label">{{ page.resolved.trusted_by.headline }}</span>
|
|
187
|
+
<div class="infinite-scroll-wrapper">
|
|
188
|
+
<div class="infinite-scroll-track">
|
|
189
|
+
{% for logo in page.resolved.trusted_by.logos %}
|
|
190
|
+
<div class="infinite-scroll-item infinite-scroll-item--logo filter-adaptive">
|
|
191
|
+
{% uj_logo logo.name, "combomarks", "original" %}
|
|
192
|
+
</div>
|
|
193
|
+
{% endfor %}
|
|
194
|
+
{% comment %} duplicate for seamless loop {% endcomment %}
|
|
195
|
+
{% for logo in page.resolved.trusted_by.logos %}
|
|
196
|
+
<div class="infinite-scroll-item infinite-scroll-item--logo filter-adaptive" aria-hidden="true">
|
|
197
|
+
{% uj_logo logo.name, "combomarks", "original" %}
|
|
198
|
+
</div>
|
|
199
|
+
{% endfor %}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</section>
|
|
205
|
+
|
|
206
|
+
<!-- ============================================ -->
|
|
207
|
+
<!-- SHOWCASE — alternating offset blocks that break the grid -->
|
|
208
|
+
<!-- ============================================ -->
|
|
209
|
+
<section class="showcase">
|
|
210
|
+
<div class="container">
|
|
211
|
+
<header class="section-head mb-5">
|
|
212
|
+
{% iftruthy page.resolved.showcase.superheadline.text %}
|
|
213
|
+
<span class="kicker mb-3">
|
|
214
|
+
{% uj_icon page.resolved.showcase.superheadline.icon, "me-1" %}{{ page.resolved.showcase.superheadline.text }}
|
|
215
|
+
</span>
|
|
216
|
+
{% endiftruthy %}
|
|
217
|
+
<h2 class="section-title">
|
|
218
|
+
{{ page.resolved.showcase.headline }}
|
|
219
|
+
<span class="text-accent">{{ page.resolved.showcase.headline_accent }}</span>
|
|
220
|
+
</h2>
|
|
221
|
+
</header>
|
|
222
|
+
|
|
223
|
+
{% assign showcase_bgs = "text-bg-primary,text-bg-secondary,text-bg-success,text-bg-info,text-bg-warning" | split: "," %}
|
|
224
|
+
<div class="showcase-list">
|
|
225
|
+
{% for item in page.resolved.showcase.items %}
|
|
226
|
+
{% assign bg = showcase_bgs[forloop.index0] | default: "text-bg-primary" %}
|
|
227
|
+
{% comment %} Liquid can't use a filter inside `if`, so compute the parity first {% endcomment %}
|
|
228
|
+
{% assign row_parity = forloop.index0 | modulo: 2 %}
|
|
229
|
+
<article class="showcase-row {% if row_parity == 1 %}showcase-row--flip{% endif %}">
|
|
230
|
+
<div class="showcase-num {{ bg }}">
|
|
231
|
+
{{ forloop.index | prepend: '0' | slice: -2, 2 }}
|
|
232
|
+
</div>
|
|
233
|
+
<div class="showcase-body">
|
|
234
|
+
<h3 class="showcase-title">{{ item.title }}</h3>
|
|
235
|
+
<p class="showcase-desc">{{ item.description }}</p>
|
|
236
|
+
</div>
|
|
237
|
+
</article>
|
|
238
|
+
{% endfor %}
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</section>
|
|
242
|
+
|
|
243
|
+
<!-- ============================================ -->
|
|
244
|
+
<!-- FEATURES — big numbered step cards -->
|
|
245
|
+
<!-- ============================================ -->
|
|
246
|
+
<section class="steps bg-body-tertiary">
|
|
247
|
+
<div class="container">
|
|
248
|
+
<header class="section-head mb-5">
|
|
249
|
+
{% iftruthy page.resolved.features.superheadline.text %}
|
|
250
|
+
<span class="kicker mb-3">
|
|
251
|
+
{% uj_icon page.resolved.features.superheadline.icon, "me-1" %}{{ page.resolved.features.superheadline.text }}
|
|
252
|
+
</span>
|
|
253
|
+
{% endiftruthy %}
|
|
254
|
+
<h2 class="section-title">
|
|
255
|
+
{{ page.resolved.features.headline }}
|
|
256
|
+
<span class="text-accent">{{ page.resolved.features.headline_accent }}</span>
|
|
257
|
+
</h2>
|
|
258
|
+
</header>
|
|
259
|
+
|
|
260
|
+
<div class="row g-4">
|
|
261
|
+
{% for feature in page.resolved.features.items %}
|
|
262
|
+
<div class="col-md-4">
|
|
263
|
+
<div class="card step-card h-100">
|
|
264
|
+
<div class="card-body">
|
|
265
|
+
<div class="step-card-top">
|
|
266
|
+
<span class="step-card-num">{{ forloop.index }}</span>
|
|
267
|
+
<span class="step-card-icon">{% uj_icon feature.icon %}</span>
|
|
268
|
+
</div>
|
|
269
|
+
<h3 class="step-card-title">{{ feature.title }}</h3>
|
|
270
|
+
<p class="step-card-desc">{{ feature.description }}</p>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
{% endfor %}
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{% iftruthy page.resolved.features.cta_button %}
|
|
278
|
+
<div class="text-center mt-5">
|
|
279
|
+
<a href="{{ page.resolved.features.cta_button.href }}" class="btn btn-warning btn-lg">
|
|
280
|
+
{% uj_icon page.resolved.features.cta_button.icon, "me-2" %}{{ page.resolved.features.cta_button.text }}
|
|
281
|
+
</a>
|
|
282
|
+
</div>
|
|
283
|
+
{% endiftruthy %}
|
|
284
|
+
</div>
|
|
285
|
+
</section>
|
|
286
|
+
|
|
287
|
+
<!-- ============================================ -->
|
|
288
|
+
<!-- STATS — oversized color-block cells -->
|
|
289
|
+
<!-- ============================================ -->
|
|
290
|
+
<section class="stats">
|
|
291
|
+
<div class="container">
|
|
292
|
+
{% assign stat_bgs = "blue:text-bg-primary,pink:text-bg-secondary,green:text-bg-success,yellow:text-bg-warning,info:text-bg-info" | split: "," %}
|
|
293
|
+
<div class="stats-grid">
|
|
294
|
+
{% for stat in page.resolved.stats %}
|
|
295
|
+
{% assign stat_bg = "text-bg-primary" %}
|
|
296
|
+
{% case stat.color %}
|
|
297
|
+
{% when "pink" %}{% assign stat_bg = "text-bg-secondary" %}
|
|
298
|
+
{% when "green" %}{% assign stat_bg = "text-bg-success" %}
|
|
299
|
+
{% when "yellow" %}{% assign stat_bg = "text-bg-warning" %}
|
|
300
|
+
{% when "info" %}{% assign stat_bg = "text-bg-info" %}
|
|
301
|
+
{% else %}{% assign stat_bg = "text-bg-primary" %}
|
|
302
|
+
{% endcase %}
|
|
303
|
+
<div class="stat-block {{ stat_bg }}">
|
|
304
|
+
<span class="stat-block-num">{{ stat.number }}</span>
|
|
305
|
+
<span class="stat-block-label">{{ stat.label }}</span>
|
|
306
|
+
{% iftruthy stat.sublabel %}<span class="stat-block-sub">{{ stat.sublabel }}</span>{% endiftruthy %}
|
|
307
|
+
</div>
|
|
308
|
+
{% endfor %}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</section>
|
|
312
|
+
|
|
313
|
+
{% include themes/neobrutalism/frontend/components/testimonial-scroll.html testimonials=page.resolved.testimonials %}
|
|
314
|
+
|
|
315
|
+
<!-- ============================================ -->
|
|
316
|
+
<!-- CTA — full-bleed ink block -->
|
|
317
|
+
<!-- ============================================ -->
|
|
318
|
+
<section class="cta">
|
|
319
|
+
<div class="container">
|
|
320
|
+
<div class="cta-panel">
|
|
321
|
+
{% iftruthy page.resolved.cta.superheadline.text %}
|
|
322
|
+
<span class="kicker kicker--invert mb-4">
|
|
323
|
+
{% uj_icon page.resolved.cta.superheadline.icon, "me-1" %}{{ page.resolved.cta.superheadline.text }}
|
|
324
|
+
</span>
|
|
325
|
+
{% endiftruthy %}
|
|
326
|
+
<h2 class="cta-title">
|
|
327
|
+
{{ page.resolved.cta.headline }}
|
|
328
|
+
<span class="text-accent">{{ page.resolved.cta.headline_accent }}</span>
|
|
329
|
+
</h2>
|
|
330
|
+
<p class="cta-desc">{{ page.resolved.cta.description }}</p>
|
|
331
|
+
<div class="d-flex flex-wrap gap-3 justify-content-center">
|
|
332
|
+
<a href="{{ page.resolved.cta.primary_button.href }}" class="btn btn-warning btn-lg">
|
|
333
|
+
{% uj_icon page.resolved.cta.primary_button.icon, "me-2" %}{{ page.resolved.cta.primary_button.text }}
|
|
334
|
+
</a>
|
|
335
|
+
{% iftruthy page.resolved.cta.secondary_button %}
|
|
336
|
+
<a href="{{ page.resolved.cta.secondary_button.href }}" class="btn btn-outline-light btn-lg">
|
|
337
|
+
{% uj_icon page.resolved.cta.secondary_button.icon, "me-2" %}{{ page.resolved.cta.secondary_button.text }}
|
|
338
|
+
</a>
|
|
339
|
+
{% endiftruthy %}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</section>
|
|
344
|
+
|
|
345
|
+
{{ content | uj_content_format }}
|