Skip to content

Commit 0006629

Browse files
committed
feat(site): add workflow sections to homepage
1 parent 983a196 commit 0006629

8 files changed

Lines changed: 1755 additions & 6 deletions

File tree

Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
<template>
2+
<section class="app-modules section">
3+
<div class="app-modules__inner container">
4+
<div class="app-modules__copy">
5+
<SectionTitle
6+
eyebrow="Application modules"
7+
title="One backend. Clear internal modules."
8+
description="Vix App Modules give growing C++ applications a visible internal structure. A backend can stay one application while features such as auth, projects, builds, packages, or billing grow inside their own module boundaries."
9+
/>
10+
11+
<div class="app-modules__text">
12+
<p>
13+
A module is not a separate service by default, and it is not a
14+
package by default. It is a feature boundary inside the application:
15+
public headers, private implementation, tests, metadata, optional
16+
migrations, and its own build target.
17+
</p>
18+
19+
<p>
20+
The application keeps the startup flow stable. Modules own
21+
feature-specific routes and implementation, while the active module
22+
graph remains visible from <code>vix.app</code>.
23+
</p>
24+
</div>
25+
26+
<div class="app-modules__features">
27+
<article
28+
v-for="feature in features"
29+
:key="feature.title"
30+
class="app-modules__feature"
31+
>
32+
<span>{{ feature.index }}</span>
33+
<strong>{{ feature.title }}</strong>
34+
<p>{{ feature.text }}</p>
35+
</article>
36+
</div>
37+
38+
<a
39+
class="app-modules__link"
40+
href="https://docs.vixcpp.com/app-modules/"
41+
target="_blank"
42+
rel="noreferrer"
43+
>
44+
Read App Modules docs
45+
<span aria-hidden="true">→</span>
46+
</a>
47+
</div>
48+
49+
<aside class="app-modules__panel" aria-label="Vix modules workflow">
50+
<div class="app-modules__panel-head">
51+
<p class="app-modules__label">vix modules</p>
52+
<strong>Feature structure that can be checked.</strong>
53+
</div>
54+
55+
<div class="app-modules__commands">
56+
<CommandLine command="vix modules init" />
57+
<CommandLine command="vix modules add auth" />
58+
<CommandLine command="vix modules add projects" />
59+
<CommandLine command="vix modules list" />
60+
<CommandLine command="vix modules check" />
61+
</div>
62+
63+
<div class="app-modules__graph">
64+
<p class="app-modules__graph-label">Example module graph</p>
65+
66+
<div
67+
v-for="item in graph"
68+
:key="item.name"
69+
class="app-modules__graph-row"
70+
>
71+
<span>{{ item.name }}</span>
72+
<strong>{{ item.depends }}</strong>
73+
</div>
74+
</div>
75+
76+
<div class="app-modules__checks">
77+
<div v-for="check in checks" :key="check" class="app-modules__check">
78+
<span aria-hidden="true" />
79+
{{ check }}
80+
</div>
81+
</div>
82+
</aside>
83+
</div>
84+
</section>
85+
</template>
86+
87+
<script setup>
88+
import CommandLine from "@/components/common/CommandLine.vue";
89+
import SectionTitle from "@/components/common/SectionTitle.vue";
90+
91+
const features = [
92+
{
93+
index: "01",
94+
title: "Feature ownership",
95+
text: "Routes, controllers, services, tests, metadata, and migrations can live with the feature they belong to.",
96+
},
97+
{
98+
index: "02",
99+
title: "Manifest graph",
100+
text: "Enabled state, module paths, kinds, and dependencies are declared from the root vix.app file.",
101+
},
102+
{
103+
index: "03",
104+
title: "Checked boundaries",
105+
text: "The CLI can detect missing modules, dependency cycles, duplicate route prefixes, and unsafe private includes.",
106+
},
107+
];
108+
109+
const graph = [
110+
{
111+
name: "auth",
112+
depends: "root module",
113+
},
114+
{
115+
name: "projects",
116+
depends: "depends on auth",
117+
},
118+
{
119+
name: "builds",
120+
depends: "depends on projects",
121+
},
122+
{
123+
name: "packages",
124+
depends: "depends on projects",
125+
},
126+
];
127+
128+
const checks = [
129+
"enabled modules exist on disk",
130+
"dependencies are declared",
131+
"route prefixes stay unique",
132+
"public headers avoid private src paths",
133+
];
134+
</script>
135+
136+
<style scoped>
137+
.app-modules {
138+
position: relative;
139+
overflow: hidden;
140+
border-block: 1px solid var(--line-soft);
141+
}
142+
143+
.app-modules::before {
144+
content: "";
145+
position: absolute;
146+
inset: 0;
147+
pointer-events: none;
148+
background:
149+
radial-gradient(
150+
circle at 80% 20%,
151+
rgba(34, 197, 94, 0.11),
152+
transparent 28rem
153+
),
154+
radial-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1px);
155+
background-size:
156+
auto,
157+
24px 24px;
158+
opacity: 0.42;
159+
mask-image: linear-gradient(#000, transparent 88%);
160+
}
161+
162+
.app-modules__inner {
163+
position: relative;
164+
display: grid;
165+
grid-template-columns: minmax(0, 1.25fr) 430px;
166+
gap: clamp(36px, 5vw, 72px);
167+
align-items: start;
168+
}
169+
170+
.app-modules__copy {
171+
min-width: 0;
172+
}
173+
174+
.app-modules__text {
175+
margin-top: 28px;
176+
display: grid;
177+
gap: 18px;
178+
max-width: 720px;
179+
}
180+
181+
.app-modules__text p {
182+
color: var(--text-soft);
183+
font-size: clamp(1rem, 1.25vw, 1.08rem);
184+
line-height: 1.82;
185+
}
186+
187+
.app-modules__text code {
188+
color: var(--green-bright);
189+
font-family: var(--font-mono);
190+
font-size: 0.92em;
191+
}
192+
193+
.app-modules__features {
194+
margin-top: 32px;
195+
display: grid;
196+
grid-template-columns: repeat(3, minmax(0, 1fr));
197+
gap: 12px;
198+
}
199+
200+
.app-modules__feature {
201+
padding: 18px;
202+
border: 1px solid var(--line);
203+
border-radius: var(--radius-md);
204+
background: rgba(255, 255, 255, 0.025);
205+
transition:
206+
border-color var(--speed) var(--ease),
207+
background var(--speed) var(--ease),
208+
transform var(--speed) var(--ease);
209+
}
210+
211+
.app-modules__feature:hover {
212+
border-color: var(--green-line);
213+
background: rgba(34, 197, 94, 0.035);
214+
transform: translateY(-2px);
215+
}
216+
217+
.app-modules__feature span {
218+
display: inline-block;
219+
color: var(--green-bright);
220+
font-family: var(--font-mono);
221+
font-size: 0.72rem;
222+
font-weight: 850;
223+
letter-spacing: 0.08em;
224+
}
225+
226+
.app-modules__feature strong {
227+
display: block;
228+
margin-top: 14px;
229+
color: var(--text);
230+
font-size: 0.94rem;
231+
font-weight: 850;
232+
letter-spacing: -0.02em;
233+
}
234+
235+
.app-modules__feature p {
236+
margin-top: 8px;
237+
color: var(--text-muted);
238+
font-size: 0.82rem;
239+
line-height: 1.58;
240+
}
241+
242+
.app-modules__link {
243+
display: inline-flex;
244+
align-items: center;
245+
gap: 7px;
246+
margin-top: 30px;
247+
color: var(--green-strong);
248+
font-family: var(--font-mono);
249+
font-size: 0.9rem;
250+
font-weight: 650;
251+
text-decoration: none;
252+
}
253+
254+
.app-modules__link:hover {
255+
color: var(--green);
256+
}
257+
258+
.app-modules__panel {
259+
position: sticky;
260+
top: 84px;
261+
overflow: hidden;
262+
border: 1px solid rgba(34, 197, 94, 0.16);
263+
border-radius: var(--radius-lg);
264+
background: var(--bg-ink);
265+
box-shadow: var(--shadow-lg);
266+
}
267+
268+
.app-modules__panel-head {
269+
padding: 24px 24px 20px;
270+
border-bottom: 1px solid rgba(34, 197, 94, 0.14);
271+
}
272+
273+
.app-modules__label {
274+
margin: 0 0 8px;
275+
color: var(--green-bright);
276+
font-family: var(--font-mono);
277+
font-size: 0.7rem;
278+
font-weight: 850;
279+
letter-spacing: 0.12em;
280+
text-transform: uppercase;
281+
}
282+
283+
.app-modules__panel-head strong {
284+
display: block;
285+
color: var(--text);
286+
font-size: 1.15rem;
287+
line-height: 1.28;
288+
letter-spacing: -0.03em;
289+
}
290+
291+
.app-modules__commands {
292+
display: grid;
293+
gap: 8px;
294+
padding: 22px 24px;
295+
border-bottom: 1px solid var(--line-soft);
296+
}
297+
298+
.app-modules__graph {
299+
padding: 22px 24px;
300+
border-bottom: 1px solid var(--line-soft);
301+
}
302+
303+
.app-modules__graph-label {
304+
margin: 0 0 14px;
305+
color: var(--text-muted);
306+
font-family: var(--font-mono);
307+
font-size: 0.7rem;
308+
font-weight: 800;
309+
letter-spacing: 0.1em;
310+
text-transform: uppercase;
311+
}
312+
313+
.app-modules__graph-row {
314+
display: grid;
315+
grid-template-columns: 110px minmax(0, 1fr);
316+
gap: 14px;
317+
align-items: center;
318+
padding: 11px 0;
319+
border-bottom: 1px solid var(--line-soft);
320+
}
321+
322+
.app-modules__graph-row:last-child {
323+
border-bottom: 0;
324+
}
325+
326+
.app-modules__graph-row span {
327+
color: var(--text);
328+
font-family: var(--font-mono);
329+
font-size: 0.86rem;
330+
font-weight: 850;
331+
}
332+
333+
.app-modules__graph-row strong {
334+
color: var(--text-muted);
335+
font-size: 0.82rem;
336+
font-weight: 650;
337+
line-height: 1.45;
338+
}
339+
340+
.app-modules__checks {
341+
display: grid;
342+
gap: 10px;
343+
padding: 22px 24px 24px;
344+
}
345+
346+
.app-modules__check {
347+
display: flex;
348+
align-items: flex-start;
349+
gap: 10px;
350+
color: var(--text-soft);
351+
font-size: 0.84rem;
352+
line-height: 1.5;
353+
}
354+
355+
.app-modules__check span {
356+
width: 7px;
357+
height: 7px;
358+
margin-top: 0.45em;
359+
border-radius: 999px;
360+
background: var(--green);
361+
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.08);
362+
flex: 0 0 auto;
363+
}
364+
365+
@media (max-width: 980px) {
366+
.app-modules__inner {
367+
grid-template-columns: 1fr;
368+
}
369+
370+
.app-modules__panel {
371+
position: relative;
372+
top: auto;
373+
max-width: 680px;
374+
}
375+
}
376+
377+
@media (max-width: 760px) {
378+
.app-modules__features {
379+
grid-template-columns: 1fr;
380+
}
381+
}
382+
383+
@media (max-width: 560px) {
384+
.app-modules__panel-head,
385+
.app-modules__commands,
386+
.app-modules__graph,
387+
.app-modules__checks {
388+
padding-inline: 20px;
389+
}
390+
391+
.app-modules__graph-row {
392+
grid-template-columns: 1fr;
393+
gap: 4px;
394+
}
395+
}
396+
</style>

0 commit comments

Comments
 (0)