@koehler8/cms 1.0.0-beta.5
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/LICENSE +21 -0
- package/README.md +202 -0
- package/bin/cms-generate-public-assets.js +27 -0
- package/bin/cms-validate-extensions.js +18 -0
- package/bin/cms-validate-themes.js +7 -0
- package/extensions/manifest.schema.json +125 -0
- package/package.json +84 -0
- package/public/img/preloaders/preloader-black.svg +1 -0
- package/public/img/preloaders/preloader-white.svg +1 -0
- package/public/robots.txt +5 -0
- package/scripts/check-overflow.mjs +33 -0
- package/scripts/generate-public-assets.js +401 -0
- package/scripts/patch-lru-cache-tla.js +164 -0
- package/scripts/validate-extensions.mjs +392 -0
- package/scripts/validate-themes.mjs +64 -0
- package/src/App.vue +3 -0
- package/src/components/About.vue +481 -0
- package/src/components/AboutValue.vue +361 -0
- package/src/components/BackToTop.vue +42 -0
- package/src/components/ComingSoon.vue +411 -0
- package/src/components/ComingSoonModal.vue +230 -0
- package/src/components/Contact.vue +518 -0
- package/src/components/Footer.vue +65 -0
- package/src/components/FooterMinimal.vue +153 -0
- package/src/components/Header.vue +583 -0
- package/src/components/Hero.vue +327 -0
- package/src/components/Home.vue +144 -0
- package/src/components/Intro.vue +130 -0
- package/src/components/IntroGate.vue +444 -0
- package/src/components/Plan.vue +116 -0
- package/src/components/Portfolio.vue +459 -0
- package/src/components/Preloader.vue +20 -0
- package/src/components/Principles.vue +67 -0
- package/src/components/Spacer15.vue +9 -0
- package/src/components/Spacer30.vue +9 -0
- package/src/components/Spacer40.vue +9 -0
- package/src/components/Spacer60.vue +9 -0
- package/src/components/StickyCTA.vue +263 -0
- package/src/components/Team.vue +432 -0
- package/src/components/icons/IconLinkedIn.vue +22 -0
- package/src/components/icons/IconX.vue +22 -0
- package/src/components/ui/SbCard.vue +52 -0
- package/src/components/ui/SkeletonPulse.vue +117 -0
- package/src/components/ui/UnitChip.vue +69 -0
- package/src/composables/useComingSoonConfig.js +120 -0
- package/src/composables/useComingSoonInterstitial.js +27 -0
- package/src/composables/useComponentResolver.js +196 -0
- package/src/composables/useEngagementTracking.js +187 -0
- package/src/composables/useIntroGate.js +46 -0
- package/src/composables/useLazyImage.js +77 -0
- package/src/composables/usePageConfig.js +184 -0
- package/src/composables/usePageMeta.js +76 -0
- package/src/composables/usePromoBackgroundStyles.js +67 -0
- package/src/constants/locales.js +20 -0
- package/src/extensions/extensionLoader.js +512 -0
- package/src/main.js +175 -0
- package/src/router/index.js +112 -0
- package/src/styles/base.css +896 -0
- package/src/styles/layout.css +342 -0
- package/src/styles/theme-base.css +84 -0
- package/src/themes/themeLoader.js +124 -0
- package/src/themes/themeManager.js +257 -0
- package/src/themes/themeValidator.js +380 -0
- package/src/utils/analytics.js +100 -0
- package/src/utils/appInfo.js +9 -0
- package/src/utils/assetResolver.js +162 -0
- package/src/utils/componentRegistry.js +46 -0
- package/src/utils/contentRequirements.js +67 -0
- package/src/utils/cookieConsent.js +281 -0
- package/src/utils/ctaCopy.js +58 -0
- package/src/utils/formatNumber.js +115 -0
- package/src/utils/imageSources.js +179 -0
- package/src/utils/inflateFlatConfig.js +30 -0
- package/src/utils/loadConfig.js +271 -0
- package/src/utils/semver.js +49 -0
- package/src/utils/siteStyles.js +40 -0
- package/src/utils/themeColors.js +65 -0
- package/src/utils/trackingContext.js +142 -0
- package/src/utils/unwrapDefault.js +14 -0
- package/src/utils/useScrollReveal.js +48 -0
- package/templates/index.html +36 -0
- package/themes/base/README.md +23 -0
- package/themes/base/theme.config.js +214 -0
- package/vite-plugin.js +637 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function createSiteStyleLoader(styleModules) {
|
|
2
|
+
let loaded = false;
|
|
3
|
+
let loadPromise;
|
|
4
|
+
|
|
5
|
+
function ensureSiteStylesLoaded() {
|
|
6
|
+
if (loadPromise) return loadPromise;
|
|
7
|
+
|
|
8
|
+
const keys = Object.keys(styleModules);
|
|
9
|
+
if (!keys.length) {
|
|
10
|
+
loadPromise = Promise.resolve(false);
|
|
11
|
+
return loadPromise;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Single site = single style module
|
|
15
|
+
const loader = styleModules[keys[0]];
|
|
16
|
+
loadPromise = Promise.resolve(loader())
|
|
17
|
+
.then(() => {
|
|
18
|
+
loaded = true;
|
|
19
|
+
return true;
|
|
20
|
+
})
|
|
21
|
+
.catch((error) => {
|
|
22
|
+
console.warn('[site-styles] Failed to load site CSS:', error);
|
|
23
|
+
return false;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return loadPromise;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { ensureSiteStylesLoaded };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---- Runtime singleton ----
|
|
33
|
+
let _ensureSiteStylesLoaded = () => Promise.resolve(false);
|
|
34
|
+
|
|
35
|
+
export function setSiteStyleLoader(instance) {
|
|
36
|
+
if (!instance) return;
|
|
37
|
+
_ensureSiteStylesLoaded = instance.ensureSiteStylesLoaded;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const ensureSiteStylesLoaded = (...args) => _ensureSiteStylesLoaded(...args);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { resolveThemeManifest } from '../themes/themeLoader.js';
|
|
2
|
+
|
|
3
|
+
const toKebab = (value = '') =>
|
|
4
|
+
value
|
|
5
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
6
|
+
.replace(/[\s_]+/g, '-')
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
|
|
9
|
+
const HEX_FALLBACK_BY_COLOR = {
|
|
10
|
+
neon_pink: '#ff2d86',
|
|
11
|
+
crimson_red: '#d9164b',
|
|
12
|
+
sultry_purple: '#9a2eff',
|
|
13
|
+
electric_blue: '#27f3ff',
|
|
14
|
+
amber_heat: '#ffaa1d',
|
|
15
|
+
deep_violet: '#5a1b9d',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const normalizeKey = (value) => value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
19
|
+
|
|
20
|
+
const derivePaletteMap = (manifest) => {
|
|
21
|
+
const map = {};
|
|
22
|
+
const palette = manifest?.tokens?.palette || {};
|
|
23
|
+
Object.entries(palette).forEach(([key, value]) => {
|
|
24
|
+
const kebab = normalizeKey(toKebab(key));
|
|
25
|
+
map[kebab] = value;
|
|
26
|
+
});
|
|
27
|
+
return map;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let activeThemeKey = 'base';
|
|
31
|
+
let activePalette = derivePaletteMap(resolveThemeManifest(activeThemeKey));
|
|
32
|
+
|
|
33
|
+
export function setActiveThemeKey(themeKey) {
|
|
34
|
+
const normalized =
|
|
35
|
+
typeof themeKey === 'string' && themeKey.trim() ? themeKey.trim().toLowerCase() : 'base';
|
|
36
|
+
activeThemeKey = normalized;
|
|
37
|
+
activePalette = derivePaletteMap(resolveThemeManifest(activeThemeKey));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveThemeColor(colorKey) {
|
|
41
|
+
if (!colorKey) return null;
|
|
42
|
+
const normalized = normalizeKey(colorKey);
|
|
43
|
+
return (
|
|
44
|
+
activePalette[normalized] ||
|
|
45
|
+
HEX_FALLBACK_BY_COLOR[colorKey.toLowerCase()] ||
|
|
46
|
+
HEX_FALLBACK_BY_COLOR[normalized] ||
|
|
47
|
+
null
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveThemePalette(colorKeys = []) {
|
|
52
|
+
if (!Array.isArray(colorKeys)) return [];
|
|
53
|
+
return colorKeys
|
|
54
|
+
.map((key) => resolveThemeColor(key))
|
|
55
|
+
.filter((value) => Boolean(value));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const DEFAULT_THEME_COLOR_ORDER = [
|
|
59
|
+
'neon_pink',
|
|
60
|
+
'sultry_purple',
|
|
61
|
+
'electric_blue',
|
|
62
|
+
'amber_heat',
|
|
63
|
+
'deep_violet',
|
|
64
|
+
'crimson_red',
|
|
65
|
+
];
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const ATTR_STORAGE_KEY = 'app_attribution_v1';
|
|
2
|
+
const SESSION_STORAGE_KEY = 'app_session_id';
|
|
3
|
+
|
|
4
|
+
function generateSessionId() {
|
|
5
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
6
|
+
return crypto.randomUUID();
|
|
7
|
+
}
|
|
8
|
+
return `sess_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function readStoredJson(key, fallback = {}) {
|
|
12
|
+
if (typeof window === 'undefined') return { ...fallback };
|
|
13
|
+
try {
|
|
14
|
+
const raw = window.sessionStorage.getItem(key);
|
|
15
|
+
if (!raw) return { ...fallback };
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
if (parsed && typeof parsed === 'object') {
|
|
18
|
+
return { ...fallback, ...parsed };
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn('[trackingContext] Failed to read storage key', key, error);
|
|
22
|
+
}
|
|
23
|
+
return { ...fallback };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeStoredJson(key, value) {
|
|
27
|
+
if (typeof window === 'undefined') return;
|
|
28
|
+
try {
|
|
29
|
+
window.sessionStorage.setItem(key, JSON.stringify(value));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.warn('[trackingContext] Failed to write storage key', key, error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function detectDeviceType(userAgent = '') {
|
|
36
|
+
const ua = userAgent.toLowerCase();
|
|
37
|
+
if (!ua) {
|
|
38
|
+
if (typeof navigator !== 'undefined' && navigator.userAgent) {
|
|
39
|
+
return detectDeviceType(navigator.userAgent);
|
|
40
|
+
}
|
|
41
|
+
return 'unknown';
|
|
42
|
+
}
|
|
43
|
+
if (/tablet|ipad|playbook|silk/.test(ua)) return 'tablet';
|
|
44
|
+
if (/mobile|iphone|ipod|android|blackberry|iemobile|opera mini/.test(ua)) return 'mobile';
|
|
45
|
+
return 'desktop';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getViewportBucket() {
|
|
49
|
+
if (typeof window === 'undefined') return 'unknown';
|
|
50
|
+
const width = window.innerWidth || 0;
|
|
51
|
+
if (width >= 1440) return 'xl';
|
|
52
|
+
if (width >= 1024) return 'lg';
|
|
53
|
+
if (width >= 768) return 'md';
|
|
54
|
+
if (width > 0) return 'sm';
|
|
55
|
+
return 'unknown';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeAttributionValue(value, fallback = 'unknown') {
|
|
59
|
+
if (!value || typeof value !== 'string') return fallback;
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
return trimmed ? trimmed : fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function persistAttributionFromLocation() {
|
|
65
|
+
if (typeof window === 'undefined') return;
|
|
66
|
+
|
|
67
|
+
const params = new URLSearchParams(window.location.search || '');
|
|
68
|
+
const stored = readStoredJson(ATTR_STORAGE_KEY, {});
|
|
69
|
+
|
|
70
|
+
const next = { ...stored };
|
|
71
|
+
|
|
72
|
+
const utmSource = normalizeAttributionValue(params.get('utm_source'), stored.utm_source || 'direct');
|
|
73
|
+
const utmMedium = normalizeAttributionValue(params.get('utm_medium'), stored.utm_medium || 'none');
|
|
74
|
+
const utmCampaign = normalizeAttributionValue(params.get('utm_campaign'), stored.utm_campaign || '');
|
|
75
|
+
const utmTerm = normalizeAttributionValue(params.get('utm_term'), stored.utm_term || '');
|
|
76
|
+
const utmContent = normalizeAttributionValue(params.get('utm_content'), stored.utm_content || '');
|
|
77
|
+
|
|
78
|
+
next.utm_source = utmSource;
|
|
79
|
+
next.utm_medium = utmMedium;
|
|
80
|
+
next.utm_campaign = utmCampaign;
|
|
81
|
+
next.utm_term = utmTerm;
|
|
82
|
+
next.utm_content = utmContent;
|
|
83
|
+
|
|
84
|
+
if (!next.first_touch_timestamp) {
|
|
85
|
+
next.first_touch_timestamp = Date.now();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!next.landing_path && window.location.pathname) {
|
|
89
|
+
next.landing_path = window.location.pathname;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!next.referrer && document.referrer) {
|
|
93
|
+
next.referrer = document.referrer;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
writeStoredJson(ATTR_STORAGE_KEY, next);
|
|
97
|
+
|
|
98
|
+
if (!window.sessionStorage.getItem(SESSION_STORAGE_KEY)) {
|
|
99
|
+
window.sessionStorage.setItem(SESSION_STORAGE_KEY, generateSessionId());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getAnalyticsContext(overrides = {}) {
|
|
104
|
+
const attribution = readStoredJson(ATTR_STORAGE_KEY, {
|
|
105
|
+
utm_source: 'direct',
|
|
106
|
+
utm_medium: 'none',
|
|
107
|
+
utm_campaign: '',
|
|
108
|
+
utm_term: '',
|
|
109
|
+
utm_content: '',
|
|
110
|
+
landing_path: typeof window !== 'undefined' ? window.location.pathname : '',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
let sessionId = '';
|
|
114
|
+
if (typeof window !== 'undefined') {
|
|
115
|
+
sessionId =
|
|
116
|
+
window.sessionStorage.getItem(SESSION_STORAGE_KEY) ||
|
|
117
|
+
(() => {
|
|
118
|
+
const fresh = generateSessionId();
|
|
119
|
+
try {
|
|
120
|
+
window.sessionStorage.setItem(SESSION_STORAGE_KEY, fresh);
|
|
121
|
+
} catch {}
|
|
122
|
+
return fresh;
|
|
123
|
+
})();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const base = {
|
|
127
|
+
utm_source: attribution.utm_source || 'direct',
|
|
128
|
+
utm_medium: attribution.utm_medium || 'none',
|
|
129
|
+
utm_campaign: attribution.utm_campaign || '',
|
|
130
|
+
utm_term: attribution.utm_term || '',
|
|
131
|
+
utm_content: attribution.utm_content || '',
|
|
132
|
+
landing_path: attribution.landing_path || '',
|
|
133
|
+
referrer: attribution.referrer || '',
|
|
134
|
+
device_type: detectDeviceType(),
|
|
135
|
+
viewport: getViewportBucket(),
|
|
136
|
+
locale: (typeof document !== 'undefined' && document.documentElement?.lang) || '',
|
|
137
|
+
session_id: sessionId,
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return { ...base, ...overrides };
|
|
142
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unwrap an ESM module's default export.
|
|
3
|
+
* Returns `module.default` when a default export exists, otherwise the module itself.
|
|
4
|
+
* Returns `undefined` for falsy inputs.
|
|
5
|
+
* @param {*} module
|
|
6
|
+
* @returns {*}
|
|
7
|
+
*/
|
|
8
|
+
export function unwrapDefault(module) {
|
|
9
|
+
if (!module) return undefined;
|
|
10
|
+
if (typeof module === 'object' && 'default' in module) {
|
|
11
|
+
return module.default;
|
|
12
|
+
}
|
|
13
|
+
return module;
|
|
14
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
let observer;
|
|
2
|
+
|
|
3
|
+
function ensureObserver() {
|
|
4
|
+
if (observer || typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {
|
|
5
|
+
return observer;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
observer = new IntersectionObserver(
|
|
9
|
+
(entries) => {
|
|
10
|
+
entries.forEach((entry) => {
|
|
11
|
+
if (entry.isIntersecting) {
|
|
12
|
+
entry.target.setAttribute('data-observed', 'true');
|
|
13
|
+
observer?.unobserve(entry.target);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
threshold: 0.2,
|
|
19
|
+
rootMargin: '0px 0px -5% 0px',
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return observer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function registerScrollReveal(elements) {
|
|
27
|
+
if (!elements) return;
|
|
28
|
+
let targets = [];
|
|
29
|
+
if (Array.isArray(elements)) {
|
|
30
|
+
targets = elements;
|
|
31
|
+
} else if (typeof elements[Symbol.iterator] === 'function' && typeof elements !== 'string') {
|
|
32
|
+
targets = Array.from(elements);
|
|
33
|
+
} else {
|
|
34
|
+
targets = [elements];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const io = ensureObserver();
|
|
38
|
+
if (!io) {
|
|
39
|
+
targets.forEach((el) => el && el.setAttribute('data-observed', 'true'));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
targets.forEach((el) => {
|
|
44
|
+
if (!el || el.dataset.observed === 'true') return;
|
|
45
|
+
el.setAttribute('data-observed', 'false');
|
|
46
|
+
io.observe(el);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title><%= site %></title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<meta name="description" content="<%= siteDescription %>">
|
|
8
|
+
<meta property="og:title" content="<%= site %>">
|
|
9
|
+
<meta property="og:description" content="<%= siteDescription %>">
|
|
10
|
+
<meta property="og:url" content="<%= siteUrl %>">
|
|
11
|
+
<meta property="og:type" content="website">
|
|
12
|
+
<meta property="og:image" content="<%= ABSOLUTE_OG_IMAGE %>">
|
|
13
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
14
|
+
<meta name="twitter:title" content="<%= site %>">
|
|
15
|
+
<meta name="twitter:description" content="<%= siteDescription %>">
|
|
16
|
+
<meta name="twitter:image" content="<%= ABSOLUTE_OG_IMAGE %>">
|
|
17
|
+
<link rel="icon" href="/favicon.ico">
|
|
18
|
+
<script type="application/ld+json">
|
|
19
|
+
{
|
|
20
|
+
"@context": "https://schema.org",
|
|
21
|
+
"@type": "Organization",
|
|
22
|
+
"name": "<%= site %>",
|
|
23
|
+
"url": "<%= siteUrl %>",
|
|
24
|
+
"logo": "<%= ABSOLUTE_LOGO %>",
|
|
25
|
+
"sameAs": <%- siteSameAsJson %>
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
</head>
|
|
29
|
+
<!-- Google Analytics is loaded dynamically after user consent -->
|
|
30
|
+
<body>
|
|
31
|
+
<main>
|
|
32
|
+
<div id="app"></div>
|
|
33
|
+
</main>
|
|
34
|
+
<script type="module" src="/src/main.js"></script>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Core Base Theme
|
|
2
|
+
- **Slug**: `base` (fallback when `site.theme` is omitted)
|
|
3
|
+
- **Version**: 1.0.0
|
|
4
|
+
- **Author**: Chris Koehler (source: internal)
|
|
5
|
+
- **Assets**: `theme.config.js` only; no additional CSS beyond shared `base.css`/`layout.css`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
- Leave `site.theme` empty to use this palette by default, or set `site.theme` to `"base"` explicitly in `sites/<site>/config/<site>.json`.
|
|
9
|
+
- Reload the dev server after changing the config so Vite rehydrates with the selected theme metadata.
|
|
10
|
+
|
|
11
|
+
## Visual Direction
|
|
12
|
+
- Neutral/light marketing canvas with deep-blue headers, soft indigo gradients, and subtle glass borders.
|
|
13
|
+
- CTA treatments lean on a cool blue gradient; secondary/ghost buttons stay minimal for operations dashboards and legal pages.
|
|
14
|
+
- Suited for general-purpose launches where you want the shared UI utilities to feel native without brand-heavy art.
|
|
15
|
+
|
|
16
|
+
## Token Highlights
|
|
17
|
+
- Balanced light surfaces (`card`, `callout`, `tabs`, `field`) tuned for dark headers/footers and sticky chrome.
|
|
18
|
+
- Consistent Inter/Clash typography stack baked into the manifest; no external CSS imports.
|
|
19
|
+
- Gradient helpers (`gradientHero`, `gradientPromo`) pair with the default hero/promo layouts without requiring per-component overrides.
|
|
20
|
+
|
|
21
|
+
## Compatibility Notes
|
|
22
|
+
- Ideal baseline for new sites before a bespoke palette is approved.
|
|
23
|
+
- Because there is no theme-specific CSS, any layout/animation customizations should live in your site’s optional `style.css` or a custom theme package.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const sharedPrimary = '#4f6cf0';
|
|
2
|
+
const sharedPrimaryDeep = '#243a80';
|
|
3
|
+
const sharedSecondary = '#5c6ac4';
|
|
4
|
+
const sharedAccent = '#f18f3b';
|
|
5
|
+
const sharedNeutral = '#f5f7ff';
|
|
6
|
+
const sharedNeutralAlt = '#edf1ff';
|
|
7
|
+
const sharedText = '#1f2a44';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
slug: 'base',
|
|
11
|
+
meta: {
|
|
12
|
+
name: 'Core Base',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
author: 'Chris Koehler',
|
|
15
|
+
source: 'internal',
|
|
16
|
+
},
|
|
17
|
+
tokens: {
|
|
18
|
+
palette: {
|
|
19
|
+
primary: sharedPrimary,
|
|
20
|
+
primaryAccent: sharedPrimaryDeep,
|
|
21
|
+
secondary: sharedSecondary,
|
|
22
|
+
accent: sharedAccent,
|
|
23
|
+
accentSoft: 'rgba(241, 143, 59, 0.22)',
|
|
24
|
+
neutral: sharedNeutral,
|
|
25
|
+
neutralStrong: sharedText,
|
|
26
|
+
neutralSoft: 'rgba(31, 42, 68, 0.85)',
|
|
27
|
+
inverse: '#ffffff',
|
|
28
|
+
success: '#239c65',
|
|
29
|
+
info: sharedPrimary,
|
|
30
|
+
warning: '#f2b05e',
|
|
31
|
+
critical: '#d04f4f',
|
|
32
|
+
criticalSoft: 'rgba(208, 79, 79, 0.18)',
|
|
33
|
+
},
|
|
34
|
+
text: {
|
|
35
|
+
primary: sharedText,
|
|
36
|
+
muted: '#54627b',
|
|
37
|
+
mutedStrong: 'rgba(31, 42, 68, 0.8)',
|
|
38
|
+
inverse: '#ffffff',
|
|
39
|
+
accent: sharedPrimary,
|
|
40
|
+
onAccent: '#ffffff',
|
|
41
|
+
},
|
|
42
|
+
surfaces: {
|
|
43
|
+
base: sharedNeutral,
|
|
44
|
+
baseAlt: sharedNeutralAlt,
|
|
45
|
+
raised: '#ffffff',
|
|
46
|
+
sunken: '#e8ecff',
|
|
47
|
+
callout: 'rgba(79, 108, 240, 0.08)',
|
|
48
|
+
card: '#ffffff',
|
|
49
|
+
cardAlt: 'rgba(255, 255, 255, 0.92)',
|
|
50
|
+
overlay: 'rgba(5, 6, 11, 0.88)',
|
|
51
|
+
backdrop: 'rgba(5, 6, 11, 0.65)',
|
|
52
|
+
border: 'rgba(79, 108, 240, 0.18)',
|
|
53
|
+
divider: 'rgba(79, 108, 240, 0.12)',
|
|
54
|
+
input: '#ffffff',
|
|
55
|
+
chip: 'rgba(79, 108, 240, 0.1)',
|
|
56
|
+
chipAccent: 'rgba(79, 108, 240, 0.16)',
|
|
57
|
+
helper: {
|
|
58
|
+
background: 'rgba(79, 108, 240, 0.08)',
|
|
59
|
+
hover: 'rgba(79, 108, 240, 0.12)',
|
|
60
|
+
text: sharedText,
|
|
61
|
+
border: 'rgba(79, 108, 240, 0.18)',
|
|
62
|
+
heading: sharedText,
|
|
63
|
+
body: 'rgba(31, 42, 68, 0.8)',
|
|
64
|
+
},
|
|
65
|
+
tabs: {
|
|
66
|
+
background: '#ffffff',
|
|
67
|
+
border: 'rgba(79, 108, 240, 0.18)',
|
|
68
|
+
shadow: '0 16px 32px rgba(15, 23, 42, 0.08)',
|
|
69
|
+
tabColor: '#54627b',
|
|
70
|
+
activeBackground: sharedPrimary,
|
|
71
|
+
activeColor: '#ffffff',
|
|
72
|
+
activeShadow: '0 16px 36px rgba(15, 23, 42, 0.12)',
|
|
73
|
+
stepBackground: 'rgba(79, 108, 240, 0.12)',
|
|
74
|
+
stepBorder: 'rgba(79, 108, 240, 0.35)',
|
|
75
|
+
stepColor: sharedPrimary,
|
|
76
|
+
activeStepBackground: sharedPrimary,
|
|
77
|
+
activeStepBorder: sharedPrimaryDeep,
|
|
78
|
+
activeStepColor: '#ffffff',
|
|
79
|
+
},
|
|
80
|
+
field: {
|
|
81
|
+
background: '#ffffff',
|
|
82
|
+
border: 'rgba(79, 108, 240, 0.25)',
|
|
83
|
+
shadow: '0 12px 24px rgba(10, 15, 30, 0.08)',
|
|
84
|
+
addonBackground: 'rgba(79, 108, 240, 0.08)',
|
|
85
|
+
addonBorder: 'rgba(79, 108, 240, 0.18)',
|
|
86
|
+
addonColor: sharedPrimary,
|
|
87
|
+
inputColor: sharedText,
|
|
88
|
+
inputPlaceholder: 'rgba(84, 98, 123, 0.64)',
|
|
89
|
+
},
|
|
90
|
+
strip: {
|
|
91
|
+
background: '#ffffff',
|
|
92
|
+
border: 'rgba(79, 108, 240, 0.18)',
|
|
93
|
+
text: sharedText,
|
|
94
|
+
},
|
|
95
|
+
chrome: {
|
|
96
|
+
background: '#05060b',
|
|
97
|
+
text: '#f0eaf3',
|
|
98
|
+
shadow: '0 18px 45px rgba(2, 3, 11, 0.6)',
|
|
99
|
+
compactShadow: '0 12px 32px rgba(2, 3, 11, 0.72)',
|
|
100
|
+
},
|
|
101
|
+
backdropPrimary: {
|
|
102
|
+
background: 'linear-gradient(135deg, #4f6cf0 0%, #243a80 100%)',
|
|
103
|
+
before: 'rgba(79, 108, 240, 0.12)',
|
|
104
|
+
after: 'rgba(36, 58, 128, 0.18)',
|
|
105
|
+
},
|
|
106
|
+
backdropSecondary: {
|
|
107
|
+
background: 'linear-gradient(135deg, #5c6ac4 0%, #4f6cf0 100%)',
|
|
108
|
+
before: 'rgba(92, 106, 196, 0.2)',
|
|
109
|
+
after: 'rgba(79, 108, 240, 0.2)',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
typography: {
|
|
113
|
+
bodyFamily: '"Inter", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
|
|
114
|
+
headingFamily:
|
|
115
|
+
'"Clash Display", "Inter", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
|
|
116
|
+
monoFamily: '"JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace',
|
|
117
|
+
baseSize: '16px',
|
|
118
|
+
scale: 1.125,
|
|
119
|
+
weightRegular: 400,
|
|
120
|
+
weightMedium: 500,
|
|
121
|
+
weightBold: 600,
|
|
122
|
+
letterSpacingTight: '-0.015em',
|
|
123
|
+
letterSpacingWide: '0.08em',
|
|
124
|
+
},
|
|
125
|
+
ctas: {
|
|
126
|
+
primary: {
|
|
127
|
+
bg: 'linear-gradient(135deg, #4f6cf0 0%, #243a80 100%)',
|
|
128
|
+
text: '#ffffff',
|
|
129
|
+
border: '1px solid rgba(255, 255, 255, 0.18)',
|
|
130
|
+
hoverBg: 'linear-gradient(135deg, #5c6ac4 0%, #243a80 100%)',
|
|
131
|
+
hoverBorder: '1px solid rgba(255, 255, 255, 0.22)',
|
|
132
|
+
shadow: '0 16px 32px rgba(32, 46, 104, 0.35)',
|
|
133
|
+
},
|
|
134
|
+
secondary: {
|
|
135
|
+
bg: '#ffffff',
|
|
136
|
+
text: sharedPrimary,
|
|
137
|
+
border: '1px solid rgba(79, 108, 240, 0.35)',
|
|
138
|
+
hoverBg: 'rgba(79, 108, 240, 0.08)',
|
|
139
|
+
hoverBorder: '1px solid rgba(79, 108, 240, 0.55)',
|
|
140
|
+
shadow: '0 8px 18px rgba(21, 32, 66, 0.18)',
|
|
141
|
+
},
|
|
142
|
+
ghost: {
|
|
143
|
+
bg: 'transparent',
|
|
144
|
+
text: sharedPrimary,
|
|
145
|
+
border: '1px solid rgba(79, 108, 240, 0.35)',
|
|
146
|
+
hoverBg: 'rgba(79, 108, 240, 0.08)',
|
|
147
|
+
hoverBorder: '1px solid rgba(79, 108, 240, 0.55)',
|
|
148
|
+
shadow: 'none',
|
|
149
|
+
},
|
|
150
|
+
link: {
|
|
151
|
+
text: sharedPrimary,
|
|
152
|
+
hoverText: sharedSecondary,
|
|
153
|
+
underline: 'rgba(79, 108, 240, 0.35)',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
chips: {
|
|
157
|
+
neutral: {
|
|
158
|
+
bg: 'rgba(79, 108, 240, 0.1)',
|
|
159
|
+
text: sharedPrimaryDeep,
|
|
160
|
+
border: 'rgba(79, 108, 240, 0.18)',
|
|
161
|
+
},
|
|
162
|
+
accent: {
|
|
163
|
+
bg: 'rgba(92, 106, 196, 0.14)',
|
|
164
|
+
text: '#ffffff',
|
|
165
|
+
border: 'rgba(92, 106, 196, 0.4)',
|
|
166
|
+
},
|
|
167
|
+
outline: {
|
|
168
|
+
bg: 'transparent',
|
|
169
|
+
text: sharedPrimary,
|
|
170
|
+
border: '1px solid rgba(79, 108, 240, 0.35)',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
focus: {
|
|
174
|
+
ring: '0 0 0 2px rgba(79, 108, 240, 0.35)',
|
|
175
|
+
ringMuted: '0 0 0 1px rgba(79, 108, 240, 0.22)',
|
|
176
|
+
outline: 'rgba(79, 108, 240, 0.6)',
|
|
177
|
+
shadowInset: 'inset 0 0 0 1px rgba(79, 108, 240, 0.25)',
|
|
178
|
+
},
|
|
179
|
+
radii: {
|
|
180
|
+
sm: '8px',
|
|
181
|
+
md: '14px',
|
|
182
|
+
lg: '24px',
|
|
183
|
+
pill: '999px',
|
|
184
|
+
full: '50%',
|
|
185
|
+
},
|
|
186
|
+
elevation: {
|
|
187
|
+
flat: '0 0 1px rgba(10, 15, 30, 0.12)',
|
|
188
|
+
raised: '0 18px 40px rgba(15, 23, 42, 0.12)',
|
|
189
|
+
overlay: '0 24px 48px rgba(15, 23, 42, 0.18)',
|
|
190
|
+
},
|
|
191
|
+
utility: {
|
|
192
|
+
divider: 'rgba(79, 108, 240, 0.12)',
|
|
193
|
+
inputBorder: 'rgba(79, 108, 240, 0.25)',
|
|
194
|
+
inputText: sharedText,
|
|
195
|
+
inputPlaceholder: 'rgba(84, 98, 123, 0.64)',
|
|
196
|
+
selectionBg: 'rgba(79, 108, 240, 0.25)',
|
|
197
|
+
selectionText: '#ffffff',
|
|
198
|
+
gradientHero: 'linear-gradient(135deg, #4f6cf0 0%, #243a80 100%)',
|
|
199
|
+
gradientPromo: 'linear-gradient(135deg, #5c6ac4 0%, #4f6cf0 100%)',
|
|
200
|
+
bodyBackground: [
|
|
201
|
+
'radial-gradient(circle at 18% 20%, rgba(79, 108, 240, 0.08), transparent 55%)',
|
|
202
|
+
'radial-gradient(circle at 80% 10%, rgba(241, 143, 59, 0.08), transparent 60%)',
|
|
203
|
+
'linear-gradient(180deg, #f8f9ff 0%, #eef1ff 100%)',
|
|
204
|
+
],
|
|
205
|
+
modalSurface: 'rgba(255, 255, 255, 0.97)',
|
|
206
|
+
modalBorder: 'rgba(79, 108, 240, 0.18)',
|
|
207
|
+
modalShadow: '0 24px 48px rgba(15, 23, 42, 0.18)',
|
|
208
|
+
modalRadius: '24px',
|
|
209
|
+
chartTrack: 'rgba(236, 241, 255, 0.85)',
|
|
210
|
+
chartCenterText: sharedText,
|
|
211
|
+
statusHeadline: 'linear-gradient(120deg, rgba(79, 108, 240, 0.12), rgba(28, 42, 96, 0.14))',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
};
|