@eox/pages-theme-eox 0.8.6 → 0.9.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eox/pages-theme-eox",
3
- "version": "0.8.6",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "description": "Vitepress Theme with EOX branding",
6
6
  "main": "src/index.js",
package/src/Layout.vue CHANGED
@@ -3,77 +3,7 @@
3
3
  <template #layout-top>
4
4
  <slot name="layout-top"></slot>
5
5
  <NavBar />
6
- <header
7
- v-if="frontmatter.hero"
8
- class="primary primary-gradient-background"
9
- >
10
- <div
11
- :class="`large-padding hero-container ${frontmatter.hero.image ? 'image' : ''}`"
12
- >
13
- <iframe
14
- v-if="frontmatter.hero.image?.src?.includes('youtube')"
15
- class="hero-image large-elevate small-round"
16
- style="grid-area: image; aspect-ratio: 16/9"
17
- :src="frontmatter.hero.image.src"
18
- frameborder="0"
19
- allowfullscreen
20
- />
21
- <video
22
- v-else-if="frontmatter.hero.video"
23
- class="hero-image large-elevate small-round"
24
- :src="withBase(frontmatter.hero.video.src)"
25
- style="grid-area: image; aspect-ratio: 16/9"
26
- autoplay
27
- muted
28
- loop
29
- />
30
- <img
31
- v-else-if="frontmatter.hero.image"
32
- class="hero-image large-elevate small-round"
33
- style="grid-area: image"
34
- :src="withBase(frontmatter.hero.image.src)"
35
- />
36
- <div class="title" style="grid-area: title; align-content: end">
37
- <h1 class="bold" style="font-size: clamp(2rem, 5vw, 45px)">
38
- {{ frontmatter.hero.text }}
39
- </h1>
40
- <p v-html="frontmatter.hero.tagline"></p>
41
- </div>
42
- <div style="grid-area: actions">
43
- <a
44
- v-for="(action, i) in frontmatter.hero.actions"
45
- :href="withBase(action.link)"
46
- :target="action.target"
47
- :rel="action.rel"
48
- class="button extra small-margin"
49
- :class="
50
- action.theme === 'brand'
51
- ? 'primary-text surface medium-elevate'
52
- : action.theme === 'secondary'
53
- ? 'secondary'
54
- : 'border no-elevate white-text'
55
- "
56
- :style="i === 0 ? 'margin-left: 0 !important' : ''"
57
- @click="
58
- trackEvent([
59
- 'CTA',
60
- 'Click',
61
- `Hero ${action.theme === 'brand' ? 'primary' : action.theme === 'secondary' ? 'secondary' : 'alt'}`,
62
- action.text,
63
- ])
64
- "
65
- >
66
- <span>{{ action.text }}</span>
67
- <i v-if="action.theme !== 'alt'" class="mdi mdi-arrow-right"></i>
68
- </a>
69
- </div>
70
- </div>
71
- <img
72
- v-if="frontmatter.hero.background"
73
- class="background-image"
74
- :src="withBase(frontmatter.hero.background.src)"
75
- />
76
- </header>
6
+ <HeroSection />
77
7
  <!-- <nav
78
8
  v-if="frontmatter.layout !== 'home'"
79
9
  class="no-space"
@@ -113,97 +43,7 @@
113
43
  ></FeaturesGallery>
114
44
  </template>
115
45
  <template #layout-bottom>
116
- <footer class="surface-container-low row center-align">
117
- <div class="holder large-padding">
118
- <div class="large-space"></div>
119
- <div class="grid">
120
- <div class="s12 l6">
121
- <img
122
- :src="withBase(theme.logo.light)"
123
- :alt="`${site.title} logo`"
124
- class="logo"
125
- />
126
- <div class="small-space"></div>
127
- <a
128
- v-if="theme.nav.find((i) => i.link.includes('contact'))"
129
- :href="theme.nav.find((i) => i.link.includes('contact')).link"
130
- class="button small border no-margin"
131
- style="color: var(--on-surface)"
132
- @click="trackEvent(['CTA', 'Click', 'Footer', action.text])"
133
- >{{ theme.nav.find((i) => i.link.includes("contact")).text }}</a
134
- >
135
- <p v-html="theme.footer.copyright"></p>
136
- <p class="middle-align">
137
- Powered by
138
- <a
139
- href="https://hub.eox.at"
140
- target="_blank"
141
- class="left-margin small-margin"
142
- ><img
143
- src="https://hub.eox.at/hub/custom/logos/eoxhub.svg"
144
- style="height: 25px"
145
- /></a>
146
- </p>
147
- </div>
148
- <div class="s12 l6">
149
- <div class="grid large-line">
150
- <div class="s6">
151
- <p class="bold">About</p>
152
- <p v-for="item in theme.nav.filter((i) => !i.action)">
153
- <a
154
- :href="withBase(item.link)"
155
- :target="item.target"
156
- :rel="item.rel"
157
- class="link"
158
- >{{ item.text }}</a
159
- >
160
- </p>
161
- </div>
162
- <div class="s6">
163
- <p class="bold">Legal</p>
164
- <p>
165
- <a
166
- :href="
167
- theme.theme.brandConfig?.legal?.about ||
168
- 'https://eox.at/impressum'
169
- "
170
- target="_blank"
171
- class="link"
172
- >About</a
173
- >
174
- </p>
175
- <p>
176
- <a
177
- :href="
178
- theme.theme.brandConfig?.legal?.termsAndConditions ||
179
- 'https://eox.at/impressum'
180
- "
181
- target="_blank"
182
- class="link"
183
- >Terms & Conditions</a
184
- >
185
- </p>
186
- <p>
187
- <a
188
- :href="
189
- theme.theme.brandConfig?.legal?.privacyPolicy ||
190
- 'https://eox.at/privacy-notice'
191
- "
192
- target="_blank"
193
- class="link"
194
- >Privacy</a
195
- >
196
- </p>
197
- <p v-if="theme.theme.brandConfig?.analytics">
198
- <a href="/cookie-settings" class="link">Cookie settings</a>
199
- </p>
200
- </div>
201
- </div>
202
- </div>
203
- </div>
204
- <div class="large-space"></div>
205
- </div>
206
- </footer>
46
+ <Footer />
207
47
  <CookieBanner v-if="theme.theme.brandConfig?.analytics"></CookieBanner>
208
48
  <slot name="layout-bottom"></slot>
209
49
  </template>
@@ -212,86 +52,13 @@
212
52
 
213
53
  <script setup>
214
54
  import DefaultTheme from "vitepress/theme";
215
- import { useData, useRouter, withBase } from "vitepress";
216
- import { trackEvent } from "./helpers";
55
+ import { useData, useRouter } from "vitepress";
217
56
  const { Layout } = DefaultTheme;
218
- const { frontmatter, page, site, theme } = useData();
57
+ const { frontmatter, page, theme } = useData();
219
58
  const router = useRouter();
220
59
  </script>
221
60
 
222
61
  <style>
223
- header {
224
- margin-top: calc(var(--vp-nav-height) * -1);
225
- padding-top: 10rem !important;
226
- padding-bottom: 10rem !important;
227
- height: calc(100svh + 2px);
228
- max-height: 1000px;
229
- }
230
- @media (max-width: 1024px) {
231
- header {
232
- padding: 0 1.5rem !important;
233
- }
234
- header h1.large {
235
- font-size: 3rem;
236
- }
237
- }
238
- header > img.background-image {
239
- position: absolute;
240
- width: 100%;
241
- height: 100%;
242
- top: 0;
243
- left: 0;
244
- object-fit: cover;
245
- opacity: 0.4;
246
- z-index: 0;
247
- border-radius: 0 !important;
248
- }
249
- header > .hero-container {
250
- max-width: 1200px;
251
- max-height: 80%;
252
- margin-left: auto;
253
- margin-right: auto;
254
- display: grid;
255
- grid-gap: 2rem;
256
- grid-template-areas:
257
- "image"
258
- "title"
259
- "actions";
260
- grid-auto-rows: min-content;
261
- text-align: center;
262
- z-index: 1;
263
- }
264
- @media (min-width: 768px) {
265
- header > .hero-container {
266
- grid-template-areas:
267
- "title"
268
- "actions";
269
- text-align: center;
270
- }
271
- header > .hero-container.image {
272
- grid-template-areas:
273
- "title image"
274
- "actions image";
275
- text-align: left;
276
- grid-template-columns: repeat(2, minmax(0, 1fr));
277
- }
278
- }
279
- header > .hero-container > .hero-image {
280
- width: 100%;
281
- object-fit: cover;
282
- }
283
- header > .hero-container > .title > h1 {
284
- margin-bottom: 24px;
285
- }
286
- header > .hero-container > .title > p {
287
- font-size: 1.2rem;
288
- }
289
- header .cta:first-child {
290
- margin-left: 0;
291
- }
292
- header .cta:last-child {
293
- margin-right: 0;
294
- }
295
62
  .top-nav > nav,
296
63
  .top-nav > .holder,
297
64
  footer > .holder,
@@ -3,7 +3,7 @@
3
3
  class="cta-section center-align full-width"
4
4
  :class="
5
5
  dark !== undefined
6
- ? 'primary primary-gradient-background'
6
+ ? 'primary primary-gradient-bg'
7
7
  : 'surface-container-low'
8
8
  "
9
9
  >
@@ -16,7 +16,7 @@
16
16
  <p class="large-text">{{ tagline }}</p>
17
17
  <a
18
18
  v-if="primaryButton"
19
- class="button extra small-margin"
19
+ class="button extra small-margin responsive-mobile"
20
20
  :class="dark !== undefined ? 'surface' : 'primary'"
21
21
  :href="primaryLink"
22
22
  @click="
@@ -26,7 +26,7 @@
26
26
  ></a>
27
27
  <a
28
28
  v-if="secondaryButton"
29
- class="button extra small-margin secondary"
29
+ class="button extra small-margin secondary responsive-mobile"
30
30
  :href="secondaryLink"
31
31
  @click="
32
32
  trackEvent(['CTA', 'Click', 'CTA Section Secondary', secondaryButton])
@@ -35,7 +35,7 @@
35
35
  ></a>
36
36
  <a
37
37
  v-if="altButton"
38
- class="button border extra small-margin"
38
+ class="button border extra small-margin responsive-mobile"
39
39
  :class="dark !== undefined ? 'white-text' : ''"
40
40
  :href="altLink"
41
41
  @click="trackEvent(['CTA', 'Click', 'CTA Section Alt', altButton])"
@@ -62,3 +62,10 @@ const props = defineProps([
62
62
  "title",
63
63
  ]);
64
64
  </script>
65
+
66
+ <style>
67
+ section.cta-section {
68
+ margin-block-start: 6rem;
69
+ margin-block-end: 6rem;
70
+ }
71
+ </style>
@@ -4,7 +4,7 @@
4
4
  >
5
5
  <div
6
6
  class="holder"
7
- :class="dark !== undefined ? 'primary-gradient-background' : ''"
7
+ :class="dark !== undefined ? 'primary-gradient-bg' : ''"
8
8
  >
9
9
  <div class="text">
10
10
  <nav class="">
@@ -22,7 +22,7 @@
22
22
  v-if="primaryLink !== undefined"
23
23
  :href="withBase(primaryLink)"
24
24
  :target="primaryLink.includes('https://') ? '_blank' : '_self'"
25
- :class="`button primary medium-elevate no-margin`"
25
+ :class="`button primary medium-elevate no-margin responsive-mobile`"
26
26
  style="margin-right: 12px !important"
27
27
  >
28
28
  <span>{{ primaryButton || `Read more about ${title}` }}</span>
@@ -32,7 +32,7 @@
32
32
  v-if="secondaryLink !== undefined"
33
33
  :href="withBase(secondaryLink)"
34
34
  :target="secondaryLink.includes('https://') ? '_blank' : '_self'"
35
- class="button secondary medium-elevate no-margin"
35
+ class="button secondary medium-elevate no-margin responsive-mobile"
36
36
  style="color: var(--on-surface); margin-top: 12px !important"
37
37
  >
38
38
  <span>{{ secondaryButton || "Contact sales" }}</span>
@@ -42,7 +42,7 @@
42
42
  v-if="altLink !== undefined"
43
43
  :href="withBase(altLink)"
44
44
  :target="altLink.includes('https://') ? '_blank' : '_self'"
45
- class="button border no-margin"
45
+ class="button border no-margin responsive-mobile"
46
46
  style="color: var(--on-surface); margin-top: 12px !important"
47
47
  >
48
48
  <span>{{ altButton || "Contact sales" }}</span>
@@ -86,3 +86,163 @@ const props = defineProps([
86
86
  "title",
87
87
  ]);
88
88
  </script>
89
+
90
+ <style>
91
+ /* Feature sections */
92
+ .full-width {
93
+ width: 100svw;
94
+ position: relative;
95
+ left: 50%;
96
+ right: 50%;
97
+ margin-left: -50svw;
98
+ margin-right: -50svw;
99
+ max-inline-size: unset !important;
100
+ }
101
+ .feature-section {
102
+ padding: 3rem 0;
103
+ display: flex;
104
+ justify-content: center;
105
+ overflow-x: hidden;
106
+ }
107
+ .feature-section .holder {
108
+ display: flex;
109
+ flex-direction: column;
110
+ align-items: center;
111
+ justify-content: space-between;
112
+ margin-left: auto;
113
+ margin-right: auto;
114
+ max-width: 1200px;
115
+ padding-bottom: 32px;
116
+ }
117
+ .feature-section.landing .holder {
118
+ padding: 32px 0;
119
+ background: var(--surface-container-low);
120
+ }
121
+ .feature-section.dark .holder {
122
+ background: radial-gradient(
123
+ farthest-corner at 100% 100%,
124
+ #0f9bff 0%,
125
+ var(--primary) 60%,
126
+ #000c14 100%
127
+ );
128
+ color: #fff;
129
+ }
130
+ .feature-section nav {
131
+ margin: 32px 0 0 0;
132
+ }
133
+ .feature-section.dark button {
134
+ background: #f7f8f8;
135
+ color: #00060a;
136
+ }
137
+ .feature-section .text {
138
+ padding: 48px;
139
+ }
140
+ .feature-section:not(.landing) .text {
141
+ padding-top: 0;
142
+ padding-left: 24px;
143
+ padding-right: 24px;
144
+ }
145
+ .feature-section .icon {
146
+ background: #61616133;
147
+ padding: 16px;
148
+ margin-bottom: 24px;
149
+ }
150
+ .feature-section.dark .icon {
151
+ background: #fff3;
152
+ }
153
+ .feature-section .title {
154
+ margin-top: 2rem;
155
+ margin-bottom: 2rem;
156
+ }
157
+ .feature-section img {
158
+ max-width: 80%;
159
+ background: var(--surface-bright);
160
+ }
161
+ @media (min-width: 1024px) {
162
+ .feature-section.landing {
163
+ padding: 64px;
164
+ padding: clamp(64px, 10%, 128px);
165
+ }
166
+ .feature-section .holder {
167
+ flex-direction: row;
168
+ padding: 68px 0;
169
+ border-radius: 12px;
170
+ }
171
+ .feature-section:not(.feature-section.landing) .holder {
172
+ padding-top: 0;
173
+ padding-bottom: 120px;
174
+ }
175
+ .feature-section img {
176
+ max-width: 60%;
177
+ transform: translateX(10%);
178
+ }
179
+ .feature-section.reverse .text {
180
+ order: 1;
181
+ }
182
+ .text:not(.feature-section.landing) .text {
183
+ padding: 0;
184
+ }
185
+ .feature-section.reverse img {
186
+ transform: translateX(-10%);
187
+ order: 0;
188
+ }
189
+ }
190
+ @media (min-width: 1280px) {
191
+ .feature-section.landing {
192
+ padding: 6rem 1.5rem;
193
+ }
194
+ .feature-section.landing .holder {
195
+ flex-direction: row;
196
+ padding: 68px 0;
197
+ }
198
+ .feature-section img {
199
+ order: 1;
200
+ max-width: 60%;
201
+ transform: translateX(10%);
202
+ }
203
+ }
204
+
205
+ /* slide/fade in images on scroll */
206
+ @media (min-width: 1024px) {
207
+ @media (prefers-reduced-motion: no-preference) {
208
+ @supports (animation-timeline: view()) {
209
+ @keyframes slide-in {
210
+ 0% {
211
+ opacity: 0;
212
+ transform: translateX(0);
213
+ }
214
+ 100% {
215
+ opacity: 1;
216
+ transform: translateX(10%);
217
+ }
218
+ }
219
+ @keyframes slide-in-reverse {
220
+ 0% {
221
+ opacity: 0;
222
+ transform: translateX(10%);
223
+ }
224
+ 100% {
225
+ opacity: 1;
226
+ transform: translateX(-10%);
227
+ }
228
+ }
229
+ .feature-section img {
230
+ --range-start: entry 0%;
231
+ --range-end: entry 150%;
232
+
233
+ animation: slide-in ease;
234
+ animation-timeline: view(block);
235
+ animation-range: var(--range-start) var(--range-end);
236
+ }
237
+ .feature-section.reverse img {
238
+ --range-start: entry 0%;
239
+ --range-end: entry 150%;
240
+
241
+ animation: slide-in-reverse ease;
242
+ animation-timeline: view(block);
243
+ animation-range: var(--range-start) var(--range-end);
244
+ }
245
+ }
246
+ }
247
+ }
248
+ </style>
@@ -24,7 +24,7 @@ const featuresExcerpts = features.map((f) => {
24
24
  </script>
25
25
 
26
26
  <template>
27
- <section class="primary primary-gradient-background full-width">
27
+ <section class="primary primary-gradient-bg full-width">
28
28
  <div class="large-space"></div>
29
29
  <div class="large-space"></div>
30
30
  <div class="holder large-padding">
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <footer class="surface-container-low row center-align">
3
+ <div class="holder large-padding">
4
+ <div class="large-space"></div>
5
+ <div class="grid">
6
+ <div class="s12 l6">
7
+ <img
8
+ :src="withBase(theme.logo.light)"
9
+ :alt="`${site.title} logo`"
10
+ class="logo"
11
+ />
12
+ <div class="small-space"></div>
13
+ <a
14
+ v-if="theme.nav.find((i) => i.link.includes('contact'))"
15
+ :href="theme.nav.find((i) => i.link.includes('contact')).link"
16
+ class="button small border no-margin"
17
+ style="color: var(--on-surface)"
18
+ @click="trackEvent(['CTA', 'Click', 'Footer', action.text])"
19
+ >{{ theme.nav.find((i) => i.link.includes("contact")).text }}</a
20
+ >
21
+ <p v-html="theme.footer.copyright"></p>
22
+ <p class="middle-align">
23
+ Powered by
24
+ <a
25
+ href="https://hub.eox.at"
26
+ target="_blank"
27
+ class="left-margin small-margin"
28
+ ><img
29
+ src="https://hub.eox.at/hub/custom/logos/eoxhub.svg"
30
+ style="height: 25px"
31
+ /></a>
32
+ </p>
33
+ </div>
34
+ <div class="s12 l6">
35
+ <div class="grid large-line">
36
+ <div class="s6">
37
+ <p class="bold">About</p>
38
+ <p v-for="item in theme.nav.filter((i) => !i.action)">
39
+ <a
40
+ :href="withBase(item.link)"
41
+ :target="item.target"
42
+ :rel="item.rel"
43
+ class="link"
44
+ >{{ item.text }}</a
45
+ >
46
+ </p>
47
+ </div>
48
+ <div class="s6">
49
+ <p class="bold">Legal</p>
50
+ <p>
51
+ <a
52
+ :href="
53
+ theme.theme.brandConfig?.legal?.about ||
54
+ 'https://eox.at/impressum'
55
+ "
56
+ target="_blank"
57
+ class="link"
58
+ >About</a
59
+ >
60
+ </p>
61
+ <p>
62
+ <a
63
+ :href="
64
+ theme.theme.brandConfig?.legal?.termsAndConditions ||
65
+ 'https://eox.at/impressum'
66
+ "
67
+ target="_blank"
68
+ class="link"
69
+ >Terms & Conditions</a
70
+ >
71
+ </p>
72
+ <p>
73
+ <a
74
+ :href="
75
+ theme.theme.brandConfig?.legal?.privacyPolicy ||
76
+ 'https://eox.at/privacy-notice'
77
+ "
78
+ target="_blank"
79
+ class="link"
80
+ >Privacy</a
81
+ >
82
+ </p>
83
+ <p v-if="theme.theme.brandConfig?.analytics">
84
+ <a href="/cookie-settings" class="link">Cookie settings</a>
85
+ </p>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ <div class="large-space"></div>
91
+ </div>
92
+ </footer>
93
+ </template>
94
+
95
+ <script setup>
96
+ import { useData, withBase } from "vitepress";
97
+ import { trackEvent } from "../helpers";
98
+ const { site, theme } = useData();
99
+ </script>
100
+
101
+ <style>
102
+ @media screen and (max-width: 600px) {
103
+ footer > .holder > .grid > div:first-child {
104
+ order: 1;
105
+ }
106
+ }
107
+ </style>
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <header v-if="frontmatter.hero" class="primary primary-gradient-bg">
3
+ <div
4
+ :class="`large-padding hero-container ${frontmatter.hero.image ? 'image' : ''}`"
5
+ >
6
+ <iframe
7
+ v-if="frontmatter.hero.image?.src?.includes('youtube')"
8
+ class="hero-image large-elevate small-round"
9
+ style="grid-area: image; aspect-ratio: 16/9"
10
+ :src="frontmatter.hero.image.src"
11
+ frameborder="0"
12
+ allowfullscreen
13
+ />
14
+ <video
15
+ v-else-if="frontmatter.hero.video"
16
+ class="hero-image large-elevate small-round"
17
+ :src="withBase(frontmatter.hero.video.src)"
18
+ style="grid-area: image; aspect-ratio: 16/9"
19
+ autoplay
20
+ muted
21
+ loop
22
+ />
23
+ <img
24
+ v-else-if="frontmatter.hero.image"
25
+ class="hero-image large-elevate small-round"
26
+ style="grid-area: image"
27
+ :src="withBase(frontmatter.hero.image.src)"
28
+ />
29
+ <div class="title" style="grid-area: title; align-content: end">
30
+ <h1 class="bold" style="font-size: clamp(2.25rem, 5vw, 3.3rem)">
31
+ {{ frontmatter.hero.text }}
32
+ </h1>
33
+ <p v-html="frontmatter.hero.tagline"></p>
34
+ </div>
35
+ <div style="grid-area: actions">
36
+ <a
37
+ v-for="(action, i) in frontmatter.hero.actions"
38
+ :href="withBase(action.link)"
39
+ :target="action.target"
40
+ :rel="action.rel"
41
+ class="button extra small-margin responsive-mobile"
42
+ :class="
43
+ action.theme === 'brand'
44
+ ? 'primary-text surface medium-elevate'
45
+ : action.theme === 'secondary'
46
+ ? 'secondary'
47
+ : 'border no-elevate white-text'
48
+ "
49
+ :style="i === 0 ? 'margin-left: 0 !important' : ''"
50
+ @click="
51
+ trackEvent([
52
+ 'CTA',
53
+ 'Click',
54
+ `Hero ${action.theme === 'brand' ? 'primary' : action.theme === 'secondary' ? 'secondary' : 'alt'}`,
55
+ action.text,
56
+ ])
57
+ "
58
+ >
59
+ <span>{{ action.text }}</span>
60
+ <i v-if="action.theme !== 'alt'" class="mdi mdi-arrow-right"></i>
61
+ </a>
62
+ </div>
63
+ </div>
64
+ <img
65
+ v-if="frontmatter.hero.background"
66
+ class="background-image"
67
+ :src="withBase(frontmatter.hero.background.src)"
68
+ />
69
+ </header>
70
+ </template>
71
+
72
+ <script setup>
73
+ import { useData, withBase } from "vitepress";
74
+ import { trackEvent } from "../helpers";
75
+ const { frontmatter } = useData();
76
+ </script>
77
+
78
+ <style>
79
+ header {
80
+ margin-top: calc(var(--vp-nav-height) * -1);
81
+ padding-top: 10rem !important;
82
+ padding-bottom: 10rem !important;
83
+ height: 76dvh;
84
+ max-height: 1000px;
85
+ }
86
+ @media (max-width: 1024px) {
87
+ header {
88
+ padding: 0 1.5rem !important;
89
+ }
90
+ header h1.large {
91
+ font-size: 3rem;
92
+ }
93
+ }
94
+ header > img.background-image {
95
+ position: absolute;
96
+ width: 100%;
97
+ height: 100%;
98
+ top: 0;
99
+ left: 0;
100
+ object-fit: cover;
101
+ opacity: 0.4;
102
+ z-index: 0;
103
+ border-radius: 0 !important;
104
+ }
105
+ header > .hero-container {
106
+ max-width: 1200px;
107
+ max-height: 80%;
108
+ margin-left: auto;
109
+ margin-right: auto;
110
+ display: grid;
111
+ grid-gap: 2rem;
112
+ grid-template-areas:
113
+ "image"
114
+ "title"
115
+ "actions";
116
+ grid-auto-rows: min-content;
117
+ text-align: center;
118
+ z-index: 1;
119
+ }
120
+ @media (min-width: 768px) {
121
+ header > .hero-container {
122
+ grid-template-areas:
123
+ "title"
124
+ "actions";
125
+ text-align: center;
126
+ }
127
+ header > .hero-container.image {
128
+ grid-template-areas:
129
+ "title image"
130
+ "actions image";
131
+ text-align: left;
132
+ grid-template-columns: repeat(2, minmax(0, 1fr));
133
+ }
134
+ }
135
+ header > .hero-container > .hero-image {
136
+ width: 100%;
137
+ object-fit: cover;
138
+ }
139
+ header > .hero-container > .title > h1 {
140
+ margin-bottom: 24px;
141
+ }
142
+ header > .hero-container > .title > p {
143
+ font-size: 1.2rem;
144
+ }
145
+ header .cta:first-child {
146
+ margin-left: 0;
147
+ }
148
+ header .cta:last-child {
149
+ margin-right: 0;
150
+ }
151
+ </style>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <section class="logo-section">
3
+ <div class="logo-row">
4
+ <component
5
+ v-for="logo in logos"
6
+ :is="logo.link ? 'a' : 'div'"
7
+ :href="logo.link || undefined"
8
+ class="logo"
9
+ >
10
+ <img :src="logo.image" :alt="logo.alt" />
11
+ </component>
12
+ </div>
13
+ </section>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { onMounted } from "vue";
18
+
19
+ const props = defineProps(["baseHeight", "logos", "strength"]);
20
+
21
+ onMounted(() => {
22
+ // Function to update CSS variables and display values
23
+ function updateVariables() {
24
+ const baseHeight = props.baseHeight || 4;
25
+ const strength = props.strength || 1;
26
+
27
+ // Update CSS variables on all .logo-row elements
28
+ const logoRows = document.querySelectorAll(".logo-row");
29
+ logoRows.forEach((logoRow) => {
30
+ logoRow.style.setProperty("--base-height", baseHeight + "rem");
31
+ logoRow.style.setProperty("--strength", strength);
32
+ logoRow.querySelectorAll("img").forEach((img) => {
33
+ img.addEventListener("load", () => {
34
+ img.parentElement.style.setProperty("--width", img.naturalWidth);
35
+ img.parentElement.style.setProperty("--height", img.naturalHeight);
36
+ img.style.opacity = 1;
37
+ });
38
+ });
39
+ });
40
+ }
41
+
42
+ // Initialize the variables on page load
43
+ updateVariables();
44
+ });
45
+ </script>
46
+
47
+ <style>
48
+ section.logo-section {
49
+ margin-block-start: 3rem;
50
+ }
51
+ .logo-section .logo-row {
52
+ --strength: 1;
53
+ --base-height: 4rem;
54
+ --logo-min-size-factor: 0.375;
55
+ --logo-max-size-factor: 1.25;
56
+
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ flex-wrap: wrap;
61
+ gap: var(--icon-gap, 2rem 3rem);
62
+ container-type: inline-size;
63
+ }
64
+ @property --captured-length {
65
+ syntax: "<length>";
66
+ initial-value: 0px;
67
+ inherits: false;
68
+ }
69
+ .logo-section .logo {
70
+ --captured-length: var(--base-height);
71
+ --area: pow(tan(atan2(var(--captured-length), 1px)), 2);
72
+ --diff: sqrt(var(--area) / (var(--width) * var(--height)));
73
+
74
+ --scaled-height: calc(1px * var(--height) * var(--diff));
75
+
76
+ height: clamp(
77
+ var(--base-height) * var(--logo-min-size-factor),
78
+ var(--base-height) + (var(--scaled-height) - var(--base-height)) *
79
+ var(--strength),
80
+ var(--base-height) * var(--logo-max-size-factor)
81
+ );
82
+
83
+ margin-block-end: calc(var(--scaled-height) * 0.4);
84
+ }
85
+
86
+ .logo-section .logo img {
87
+ max-width: 100%;
88
+ height: 100%;
89
+
90
+ opacity: 0;
91
+ transition: opacity 0.3s ease-in-out;
92
+ }
93
+
94
+ @media screen and (max-width: 600px) {
95
+ .logo-section .logo-row {
96
+ justify-content: center;
97
+ }
98
+ }
99
+ </style>
@@ -161,7 +161,6 @@ if (!import.meta.env.SSR) {
161
161
  inset 0 0 0 1px #ffffff80;
162
162
  display: flex;
163
163
  width: 100%;
164
- transition: all 0.3s ease-in-out;
165
164
  }
166
165
  .top-nav.at-top {
167
166
  box-shadow: none;
@@ -174,9 +173,13 @@ if (!import.meta.env.SSR) {
174
173
  .Layout.layout-home .top-nav.at-top nav.actions {
175
174
  gap: 0;
176
175
  }
176
+ .top-nav,
177
+ .top-nav > nav {
178
+ transition: all 0.3s ease-in-out;
179
+ }
177
180
  .top-nav:not(.at-top) > nav {
178
- padding-top: 16px !important;
179
- padding-bottom: 16px !important;
181
+ padding-top: 0.5rem !important;
182
+ padding-bottom: 0.5rem !important;
180
183
  }
181
184
  .Layout.layout-home .top-nav.at-top {
182
185
  background: transparent;
@@ -4,7 +4,7 @@
4
4
  <p>The page you requested was not found.</p>
5
5
  <div class="small-space"></div>
6
6
  <nav>
7
- <a class="button" href="/">
7
+ <a class="button responsive-mobile" href="/">
8
8
  <i class="mdi mdi-arrow-left"></i>
9
9
  <span>Back to home</span>
10
10
  </a>
@@ -27,8 +27,13 @@
27
27
  </div>
28
28
  </div>
29
29
  <div
30
- class="grid m l surface-bright"
31
- style="position: sticky; top: 72px; z-index: 1000; padding-top: 20px"
30
+ class="grid l surface-bright"
31
+ style="
32
+ position: sticky;
33
+ top: var(--vp-nav-height);
34
+ z-index: 1000;
35
+ padding-top: 20px;
36
+ "
32
37
  v-if="localDetails.length"
33
38
  >
34
39
  <div class="s12 m3"></div>
@@ -52,7 +57,7 @@
52
57
  <h6 v-if="localDetails.length" class="bold padding-4">{{ title }}</h6>
53
58
  <!-- Main Plans Table -->
54
59
  <div class="wrapper" :style="gridStyle">
55
- <div class="cell orig-col-1 m l top-margin">
60
+ <div class="cell orig-col-1 l top-margin">
56
61
  <h6 v-if="!localDetails.length" class="bold small">Plans:</h6>
57
62
  </div>
58
63
  <div
@@ -64,7 +69,7 @@
64
69
  <h6 class="primary-text bold top-margin">{{ plan.name }}</h6>
65
70
  </div>
66
71
 
67
- <div class="cell orig-col-1 m l">Price (per month):</div>
72
+ <div class="cell orig-col-1 l">Price (per month):</div>
68
73
  <div
69
74
  v-for="(plan, index) in localPlans"
70
75
  :key="'price-' + index"
@@ -82,7 +87,7 @@
82
87
  >
83
88
  <div
84
89
  v-if="typeof row === 'string'"
85
- class="cell orig-col-1 m l"
90
+ class="cell orig-col-1 l"
86
91
  v-html="row + ':'"
87
92
  ></div>
88
93
 
@@ -94,7 +99,7 @@
94
99
  >
95
100
  <div
96
101
  v-if="typeof plan.details[row] === 'string'"
97
- class="s grey-text"
102
+ class="s m grey-text"
98
103
  v-html="row"
99
104
  ></div>
100
105
  <div
@@ -130,7 +135,7 @@
130
135
  </template>
131
136
  </div>
132
137
  </template>
133
- <div class="cell orig-col-1 m l"></div>
138
+ <div class="cell orig-col-1 l"></div>
134
139
  <div
135
140
  v-for="(plan, index) in localPlans"
136
141
  :key="'cta-' + index"
@@ -151,7 +156,7 @@
151
156
  </a>
152
157
  </div>
153
158
  <!-- Contact us section -->
154
- <div class="cell orig-col-1 m l"></div>
159
+ <div class="cell orig-col-1 l"></div>
155
160
  <div
156
161
  v-if="showSales"
157
162
  v-for="(plan, index) in localPlans"
@@ -179,7 +184,7 @@
179
184
  </div>
180
185
 
181
186
  <div class="wrapper" :style="secondaryGridStyle">
182
- <div class="cell cell orig-col-1 m l">
187
+ <div class="cell cell orig-col-1 l">
183
188
  <div class="">Additional price:</div>
184
189
  </div>
185
190
  <div
@@ -199,7 +204,7 @@
199
204
  >
200
205
  <div
201
206
  v-if="typeof row === 'string'"
202
- class="cell orig-col-1 m l"
207
+ class="cell orig-col-1 l"
203
208
  v-html="row + ':'"
204
209
  ></div>
205
210
  <div
@@ -208,7 +213,7 @@
208
213
  class="bold surface-container-low"
209
214
  :class="`cell orig-col-${planIndex + 2} ${rowIndex === getRowKeys(detail.plans).length - 1 && !config.showSales ? 'bottom-cell' : ''}`"
210
215
  >
211
- <div class="s grey-text" v-html="row + ':'"></div>
216
+ <div class="s m grey-text" v-html="row + ':'"></div>
212
217
  <template
213
218
  v-if="
214
219
  typeof plan.details[row] === 'object' &&
@@ -257,7 +262,7 @@ export default {
257
262
  localOptions: [],
258
263
  localPlans: [],
259
264
  localDetails: [],
260
- isMobile: !import.meta.env.SSR && window.innerWidth <= 768,
265
+ isMobile: false,
261
266
  };
262
267
  },
263
268
  computed: {
@@ -320,10 +325,12 @@ export default {
320
325
  this.localDetails = this.config.details ?? this.localDetails;
321
326
  },
322
327
  handleResize() {
323
- this.isMobile = window.innerWidth <= 768;
328
+ if (typeof window !== "undefined") {
329
+ this.isMobile = window.innerWidth <= 992;
330
+ }
324
331
  },
325
332
  updateOrderStyles() {
326
- if (import.meta.env.SSR) {
333
+ if (typeof window === "undefined") {
327
334
  return;
328
335
  }
329
336
  const count = this.localPlans.length + 1;
@@ -335,8 +342,8 @@ export default {
335
342
  }
336
343
  },
337
344
  addPlanConfig(contactLink, plan) {
338
- if (import.meta.env.SSR) {
339
- return "";
345
+ if (typeof document === "undefined") {
346
+ return contactLink; // Return original link as fallback
340
347
  }
341
348
  const parts = contactLink.split("?");
342
349
  let search = parts[1] || "";
@@ -372,7 +379,9 @@ export default {
372
379
  mounted() {
373
380
  window.addEventListener("resize", this.handleResize);
374
381
  this.handleResize();
375
- this.updateOrderStyles();
382
+ this.$nextTick(() => {
383
+ this.updateOrderStyles();
384
+ });
376
385
  },
377
386
  beforeDestroy() {
378
387
  window.removeEventListener("resize", this.handleResize);
@@ -403,9 +412,7 @@ export default {
403
412
  deep: true,
404
413
  },
405
414
  isMobile() {
406
- if (!import.meta.env.SSR) {
407
- this.updateOrderStyles();
408
- }
415
+ this.updateOrderStyles();
409
416
  },
410
417
  },
411
418
  };
@@ -437,7 +444,7 @@ export default {
437
444
  border-bottom-right-radius: 0.5rem;
438
445
  }
439
446
 
440
- @media screen and (min-width: 769px) {
447
+ @media screen and (min-width: 992px) {
441
448
  .wrapper {
442
449
  column-gap: 20px;
443
450
  row-gap: 0;
package/src/index.js CHANGED
@@ -1,9 +1,12 @@
1
1
  import DefaultTheme from "vitepress/theme";
2
2
  import NavBar from "./components/NavBar.vue";
3
+ import Footer from "./components/Footer.vue";
4
+ import HeroSection from "./components/HeroSection.vue";
3
5
  import FeatureSection from "./components/FeatureSection.vue";
4
6
  import FeaturesGallery from "./components/FeaturesGallery.vue";
5
7
  import PricingTable from "./components/PricingTable.vue";
6
8
  import CTASection from "./components/CTASection.vue";
9
+ import LogoSection from "./components/LogoSection.vue";
7
10
  import CookieBanner from "./components/CookieBanner.vue";
8
11
  import CookieSettings from "./components/CookieSettings.vue";
9
12
  import NotFound from "./components/NotFound.vue";
@@ -15,10 +18,13 @@ export default {
15
18
  Layout,
16
19
  enhanceApp({ app, router, siteData }) {
17
20
  app.component("NavBar", NavBar);
21
+ app.component("Footer", Footer);
22
+ app.component("HeroSection", HeroSection);
18
23
  app.component("FeatureSection", FeatureSection);
19
24
  app.component("FeaturesGallery", FeaturesGallery);
20
25
  app.component("PricingTable", PricingTable);
21
26
  app.component("CTASection", CTASection);
27
+ app.component("LogoSection", LogoSection);
22
28
  app.component("CookieBanner", CookieBanner);
23
29
  app.component("CookieSettings", CookieSettings);
24
30
  app.component("NotFound", NotFound);
package/src/style.css CHANGED
@@ -1,7 +1,7 @@
1
1
  @import "@eox/ui/style.css";
2
2
 
3
3
  :root {
4
- --vp-nav-height: 74px;
4
+ --vp-nav-height: 58px;
5
5
  }
6
6
  :root:has(.top-nav.at-top) {
7
7
  --vp-nav-height: 90px;
@@ -19,6 +19,10 @@ body {
19
19
  display: none;
20
20
  }
21
21
 
22
+ .VPContent {
23
+ font-size: 1rem;
24
+ }
25
+
22
26
  .VPContent a.button {
23
27
  text-decoration: none;
24
28
  }
@@ -49,6 +53,10 @@ body {
49
53
  width: 100%;
50
54
  padding: 48px 64px;
51
55
  }
56
+
57
+ .VPContent {
58
+ margin-top: calc(var(--vp-nav-height) * -1) !important;
59
+ }
52
60
  }
53
61
 
54
62
  @media (min-width: 640px) {
@@ -147,164 +155,15 @@ button.text:hover,
147
155
  border-color: var(--outline-variant);
148
156
  }
149
157
 
150
- /* Feature sections */
151
- .full-width {
152
- width: 100svw;
153
- position: relative;
154
- left: 50%;
155
- right: 50%;
156
- margin-left: -50svw;
157
- margin-right: -50svw;
158
- max-inline-size: unset;
159
- }
160
- .feature-section {
161
- padding: 0px;
162
- display: flex;
163
- justify-content: center;
164
- }
165
- .feature-section .holder {
166
- display: flex;
167
- flex-direction: column;
168
- align-items: center;
169
- justify-content: space-between;
170
- margin-left: auto;
171
- margin-right: auto;
172
- max-width: 1200px;
173
- padding-bottom: 32px;
174
- }
175
- .feature-section.landing .holder {
176
- padding: 32px 0;
177
- background: var(--surface-container-low);
178
- }
179
- .feature-section.dark .holder {
180
- background: radial-gradient(
181
- farthest-corner at 100% 100%,
182
- #0f9bff 0%,
183
- var(--primary) 60%,
184
- #000c14 100%
185
- );
186
- color: #fff;
187
- }
188
- .feature-section nav {
189
- margin: 32px 0 0 0;
190
- }
191
- .feature-section.dark button {
192
- background: #f7f8f8;
193
- color: #00060a;
194
- }
195
- .feature-section .text {
196
- padding: 48px;
197
- }
198
- .feature-section:not(.landing) .text {
199
- padding-top: 0;
200
- padding-left: 24px;
201
- padding-right: 24px;
202
- }
203
- .feature-section .icon {
204
- background: #61616133;
205
- padding: 16px;
206
- margin-bottom: 24px;
207
- }
208
- .feature-section.dark .icon {
209
- background: #fff3;
210
- }
211
- .feature-section .title {
212
- margin-top: 2rem;
213
- margin-bottom: 2rem;
214
- }
215
- .feature-section img {
216
- max-width: 80%;
217
- background: var(--surface-bright);
218
- }
219
- @media (min-width: 1024px) {
220
- .feature-section.landing {
221
- padding: 64px;
222
- padding: clamp(64px, 10%, 128px);
223
- }
224
- .feature-section .holder {
225
- flex-direction: row;
226
- padding: 68px 0;
227
- border-radius: 12px;
228
- }
229
- .feature-section:not(.feature-section.landing) .holder {
230
- padding-top: 0;
231
- padding-bottom: 120px;
232
- }
233
- .feature-section img {
234
- max-width: 60%;
235
- transform: translateX(10%);
236
- }
237
- .feature-section.reverse .text {
238
- order: 1;
239
- }
240
- .text:not(.feature-section.landing) .text {
241
- padding: 0;
242
- }
243
- .feature-section.reverse img {
244
- transform: translateX(-10%);
245
- order: 0;
246
- }
247
- }
248
- @media (min-width: 1280px) {
249
- .feature-section.landing {
250
- padding: 128px 24px;
251
- }
252
- .feature-section.landing .holder {
253
- flex-direction: row;
254
- padding: 68px 0;
255
- }
256
- .feature-section img {
257
- order: 1;
258
- max-width: 60%;
259
- transform: translateX(10%);
260
- }
261
- }
262
-
263
- /* slide/fade in images on scroll */
264
- @media (min-width: 1024px) {
265
- @media (prefers-reduced-motion: no-preference) {
266
- @supports (animation-timeline: view()) {
267
- @keyframes slide-in {
268
- 0% {
269
- opacity: 0;
270
- transform: translateX(0);
271
- }
272
- 100% {
273
- opacity: 1;
274
- transform: translateX(10%);
275
- }
276
- }
277
- @keyframes slide-in-reverse {
278
- 0% {
279
- opacity: 0;
280
- transform: translateX(10%);
281
- }
282
- 100% {
283
- opacity: 1;
284
- transform: translateX(-10%);
285
- }
286
- }
287
- .feature-section img {
288
- --range-start: entry 0%;
289
- --range-end: entry 150%;
290
-
291
- animation: slide-in ease;
292
- animation-timeline: view(block);
293
- animation-range: var(--range-start) var(--range-end);
294
- }
295
- .feature-section.reverse img {
296
- --range-start: entry 0%;
297
- --range-end: entry 150%;
298
-
299
- animation: slide-in-reverse ease;
300
- animation-timeline: view(block);
301
- animation-range: var(--range-start) var(--range-end);
302
- }
303
- }
158
+ /* Add responsive-mobile class for buttons that should only be responsive on mobile */
159
+ @media screen and (max-width: 600px) {
160
+ .button.responsive-mobile {
161
+ inline-size: -webkit-fill-available;
162
+ inline-size: -moz-available;
304
163
  }
305
164
  }
306
165
 
307
- .primary-gradient-background {
166
+ .primary-gradient-bg {
308
167
  background: radial-gradient(
309
168
  74.48% 130.95% at 50% -12.27%,
310
169
  hsl(from var(--primary) h s calc(l + 7)) 0%,
@@ -312,8 +171,3 @@ button.text:hover,
312
171
  hsl(from var(--primary) h s calc(l - 20)) 100%
313
172
  ) !important;
314
173
  }
315
-
316
- header[class*="background"],
317
- section[class*="background"] {
318
- border-radius: 0 !important;
319
- }