Skip to content

Commit 122919e

Browse files
kieronlanningCopilotCopilotCopilot
authored
feat: reduce HeroBanner size, add tabbed content, update AspireC4 (#58)
* feat: migrate toolchain, add AspireC4 project, theme improvements - Replace Bun with npm throughout (package.json, workflows, instructions) - Replace husky/hereby with just + lefthook for git hooks and task running - Update all GitHub Actions to latest major versions (checkout v6, setup-node v6, etc.) - Fix deprecated z import: use astro/zod instead of astro:content - Add emoji support to links schema (XOR with image via Zod refine) - Add markdown linting with markdownlint-cli2 and lefthook integration - Rebuild site styles: complete dark/light/system theme, mobile-first, accessible - Add sidebar with ToC and project list on project detail pages - Limit MainBanner and hero heights on wide screens - Add AspireC4 project content entry with hasContent + remote README stacking - Add GitHub README badge to RemoteReadme component - Fix likec4-wordmark.svg theme responsiveness via colorScheme property - Lock Vite to <8 as per deprecation warning - Fix 'Say Hello' contact dialog with captcha support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: pin tailwindcss and @tailwindcss/vite to ~4.1.12 to fix Vite 7 compatibility @tailwindcss/vite@4.3.0 requires Rolldown (Vite 8 internals) which is incompatible with astro@6.3.1's Vite 7 dependency. Pinning to the 4.1.x patch range keeps compatibility until Astro upgrades to Vite 8. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: reduce HeroBanner size, add tabbed content, update AspireC4 - Reduce HeroBanner vertical padding and font sizes so content is visible above the fold on wide screens - Add ContentTabs.astro component: tabbed Overview/GitHub README view for project pages that have both local content and a remote README - GitHub logo icon and cursor-pointer on tab buttons - Update AspireC4 project content and add makerstack example images - Tidy MainBanner spacing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: fixed working Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * style: align MainBanner Astro attribute quoting Agent-Logs-Url: https://github.com/kjldev/kjldev/sessions/bbda4125-85f2-49f0-a870-b3b6dbc5db04 Co-authored-by: kieronlanning <5364423+kieronlanning@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kieronlanning <5364423+kieronlanning@users.noreply.github.com>
1 parent f9001c9 commit 122919e

7 files changed

Lines changed: 202 additions & 31 deletions

File tree

src/src/collections/projects/AspireC4.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,57 @@ tags:
3030
- diagrams-as-code
3131
hasContent: true
3232
---
33-
AspireC4 is an extension library for the Aspire framework that generates live C4 architecture diagrams using LikeC4.
33+
AspireC4 is an extension library for the [Aspire][2] framework that generates live C4 architecture diagrams using LikeC4.
3434

3535
It provides real-time visualizations of your system's architecture, automatically updating as resources change state. Each element in the diagram links back to the Aspire dashboard for easy navigation and monitoring.
3636

3737
You can find the source code and installation instructions on the [GitHub repository](https://github.com/kjldev/aspirec4) and the package is available on [NuGet](https://www.nuget.org/packages/AspireC4.Hosting/).
3838

3939
For more information on LikeC4, visit the [LikeC4 website][1].
4040

41-
All of this was made possible by the excellent hardwork for the Aspire and LikeC4 teams, and the open-source community. AspireC4 is licensed under the MIT License, allowing you to freely use, modify, and distribute it in your projects.
41+
All of this was made possible by the excellent hard work of the Aspire and LikeC4 teams, and the rest of the open-source community. AspireC4 is licensed under the MIT License, allowing you to freely use, modify, and distribute it in your projects.
4242

43-
[![LikeC4](aspirec4/likec4-wordmark.svg)][1]
44-
[![Aspire](shared-assets/aspire-logo.svg)][2]
43+
## Examples
44+
45+
Here is a basic example of AspireC4 in action, showing a live architecture diagram generated from an Aspire resource graph using only the `builder.AddAspireC4()` extension method:
46+
47+
![Basic Example](aspirec4/makerstack-basic.png)
48+
49+
With a few additional configuration options, you can customize the diagram's appearance and behaviour:
50+
51+
```csharp
52+
// AppHost/Program.cs
53+
var builder = DistributedApplication.CreateBuilder(args);
54+
builder.AddAspireC4(opts => {
55+
opts.Title = "MakerStack Architecture";
56+
});
57+
58+
// ...add other projects/resources such as databases, services, etc.
59+
builder
60+
.AddNodeApp("frontend", "../../frontend")
61+
.WithLikeC4Details(opts => {
62+
opts
63+
.WithLabel("MakerStack React SPA")
64+
.WithIcon("tech:react")
65+
... etc
66+
});
67+
68+
builder
69+
.AddProject<Projects.Web>("web")
70+
.WithLikeC4Details(opts => {
71+
opts
72+
.WithLabel("Web Host")
73+
.WithSummary(".NET 10/ ASP.NET Minimal API web app. Hosting APIs, and the Astro-based landing site and the MarkerStack SPA");
74+
})
75+
# Note the reference to the database project here, to save you from calling .WithReference(db) as well..
76+
.WithLikeC4Reference(db, opts => {
77+
opts
78+
.WithLabel("Tabular Data Stream (TDS)")
79+
... etc
80+
});
81+
```
82+
83+
![Enhanced integration](aspirec4/makerstack-enhanced.png)
4584

4685
[1]: https://likec4.dev/
4786
[2]: https://aspire.dev/
265 KB
Loading
346 KB
Loading
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
interface Props {
3+
overviewLabel?: string;
4+
readmeLabel?: string;
5+
}
6+
7+
const { overviewLabel = 'Overview', readmeLabel = 'GitHub README' } = Astro.props;
8+
9+
const tabId = `tabs-${Math.random().toString(36).slice(2, 8)}`;
10+
---
11+
12+
<div
13+
class='content-tabs'
14+
data-tabs-root
15+
id={tabId}
16+
>
17+
<!-- Tab bar -->
18+
<div
19+
role='tablist'
20+
aria-label='Content tabs'
21+
class='flex gap-1 border-b border-gray-200 dark:border-gray-700 mb-6'
22+
>
23+
<button
24+
type='button'
25+
role='tab'
26+
data-tab-btn='overview'
27+
aria-selected='true'
28+
aria-controls={`${tabId}-panel-overview`}
29+
id={`${tabId}-tab-overview`}
30+
class='tab-btn cursor-pointer px-4 py-2 text-sm font-medium rounded-t-md border border-b-0 -mb-px transition-colors
31+
border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900
32+
text-blue-600 dark:text-blue-400'
33+
>
34+
{overviewLabel}
35+
</button>
36+
<button
37+
type='button'
38+
role='tab'
39+
data-tab-btn='readme'
40+
aria-selected='false'
41+
aria-controls={`${tabId}-panel-readme`}
42+
id={`${tabId}-tab-readme`}
43+
class='tab-btn cursor-pointer inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-t-md border border-b-0 -mb-px transition-colors
44+
border-transparent bg-transparent
45+
text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'
46+
>
47+
<svg
48+
viewBox='0 0 24 24'
49+
class='h-4 w-4 fill-current shrink-0'
50+
aria-hidden='true'
51+
>
52+
<path d='M12 0C5.373 0 0 5.373 0 12c0 5.303 3.438 9.8 8.205 11.387.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.756-1.333-1.756-1.09-.745.083-.729.083-.729 1.205.084 1.84 1.237 1.84 1.237 1.07 1.834 2.807 1.304 3.492.997.108-.775.418-1.304.762-1.604-2.665-.3-5.467-1.332-5.467-5.931 0-1.31.468-2.381 1.235-3.221-.123-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23a11.51 11.51 0 013.003-.404c1.02.005 2.045.138 3.003.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.12 3.176.77.84 1.233 1.911 1.233 3.221 0 4.609-2.807 5.628-5.479 5.921.43.371.823 1.102.823 2.222 0 1.604-.015 2.896-.015 3.289 0 .322.216.694.825.576C20.565 21.795 24 17.303 24 12c0-6.627-5.373-12-12-12' />
53+
</svg>
54+
{readmeLabel}
55+
</button>
56+
</div>
57+
58+
<!-- Tab panels -->
59+
<div
60+
role='tabpanel'
61+
id={`${tabId}-panel-overview`}
62+
aria-labelledby={`${tabId}-tab-overview`}
63+
data-tab-panel='overview'
64+
>
65+
<slot name='overview' />
66+
</div>
67+
68+
<div
69+
role='tabpanel'
70+
id={`${tabId}-panel-readme`}
71+
aria-labelledby={`${tabId}-tab-readme`}
72+
data-tab-panel='readme'
73+
hidden
74+
>
75+
<slot name='readme' />
76+
</div>
77+
</div>
78+
79+
<script>
80+
function initTabs(root: HTMLElement) {
81+
const buttons = root.querySelectorAll<HTMLButtonElement>('[data-tab-btn]');
82+
const panels = root.querySelectorAll<HTMLElement>('[data-tab-panel]');
83+
84+
buttons.forEach((btn) => {
85+
btn.addEventListener('click', () => {
86+
const target = btn.dataset.tabBtn!;
87+
88+
buttons.forEach((b) => {
89+
const active = b.dataset.tabBtn === target;
90+
b.setAttribute('aria-selected', String(active));
91+
if (active) {
92+
b.classList.add(
93+
'border-gray-200',
94+
'dark:border-gray-700',
95+
'bg-white',
96+
'dark:bg-gray-900',
97+
'text-blue-600',
98+
'dark:text-blue-400'
99+
);
100+
b.classList.remove(
101+
'border-transparent',
102+
'bg-transparent',
103+
'text-gray-500',
104+
'dark:text-gray-400',
105+
'hover:text-gray-700',
106+
'dark:hover:text-gray-200'
107+
);
108+
} else {
109+
b.classList.remove(
110+
'border-gray-200',
111+
'dark:border-gray-700',
112+
'bg-white',
113+
'dark:bg-gray-900',
114+
'text-blue-600',
115+
'dark:text-blue-400'
116+
);
117+
b.classList.add(
118+
'border-transparent',
119+
'bg-transparent',
120+
'text-gray-500',
121+
'dark:text-gray-400',
122+
'hover:text-gray-700',
123+
'dark:hover:text-gray-200'
124+
);
125+
}
126+
});
127+
128+
panels.forEach((panel) => {
129+
panel.hidden = panel.dataset.tabPanel !== target;
130+
});
131+
});
132+
});
133+
}
134+
135+
document.querySelectorAll<HTMLElement>('[data-tabs-root]').forEach(initTabs);
136+
</script>

src/src/components/HeroBanner.astro

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const { backgroundImgSrc, title, subtitle } = Astro.props;
1212

1313
<section
1414
aria-label={title}
15-
class='w-full flex items-center justify-center relative overflow-hidden py-16 sm:py-20 lg:py-24'
15+
class='w-full flex items-center justify-center relative overflow-hidden py-8 sm:py-10 lg:py-12'
1616
>
1717
<!-- Background image (decorative) -->
1818
<div
@@ -31,22 +31,22 @@ const { backgroundImgSrc, title, subtitle } = Astro.props;
3131
<!-- Content container -->
3232
<div class='relative z-10 w-full max-w-4xl mx-auto px-4 sm:px-6'>
3333
<div
34-
class='bg-white/95 dark:bg-gray-900/90 backdrop-blur-sm rounded-2xl shadow-2xl border-2 border-blue-200 dark:border-purple-500 p-6 sm:p-8 lg:p-12'
34+
class='bg-white/95 dark:bg-gray-900/90 backdrop-blur-sm rounded-2xl shadow-2xl border-2 border-blue-200 dark:border-purple-500 p-4 sm:p-6 lg:p-8'
3535
>
3636
<h1
37-
class='text-3xl sm:text-4xl lg:text-5xl font-bold mb-4 sm:mb-6 text-gray-900 dark:text-white'
37+
class='text-2xl sm:text-3xl lg:text-4xl font-bold mb-3 sm:mb-4 text-gray-900 dark:text-white'
3838
>
3939
{title}
4040
</h1>
4141
{
4242
subtitle && (
43-
<p class='text-lg sm:text-xl lg:text-2xl mb-6 sm:mb-8 text-gray-600 dark:text-gray-200 font-medium'>
43+
<p class='text-base sm:text-lg lg:text-xl mb-4 sm:mb-6 text-gray-600 dark:text-gray-200 font-medium'>
4444
{subtitle}
4545
</p>
4646
)
4747
}
4848
<div
49-
class='text-gray-700 dark:text-gray-100 text-base sm:text-lg leading-relaxed'
49+
class='text-gray-700 dark:text-gray-100 text-sm sm:text-base leading-relaxed'
5050
>
5151
<slot />
5252
</div>

src/src/components/index/MainBanner.astro

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import HeroBanner from '../HeroBanner.astro';
33
import SayHello from '../SayHello.tsx';
44
import backgroundImg from '../../assets/index-bg.jpg';
5+
import Pill from '../Pill.astro';
56
---
67

78
<HeroBanner
@@ -10,23 +11,16 @@ import backgroundImg from '../../assets/index-bg.jpg';
1011
subtitle='Accelerating your projects and teams.'
1112
>
1213
<p class='mb-5'>
13-
Providing consultancy services tailored to you and your needs through
14-
technology and processes, to accelerate teams and businesses at any stage of
15-
growth.
14+
Providing consultancy services tailored to you and your needs through technology and processes, to accelerate teams
15+
and businesses at any stage of growth.
1616
</p>
1717
<p class='mb-10'>
18-
With a history of helping companies of all sizes, from start-ups to
19-
enterprises, to get their digital deliveries off the whiteboard or get that
20-
project back on track.
18+
With a history of helping companies of all sizes, from start-ups to enterprises, to get their digital deliveries off
19+
the whiteboard or get that project back on track.
2120
</p>
2221
<SayHello client:load />
2322
<p class='mt-5'>
2423
Don't forget to check out our
25-
<a
26-
href='/projects'
27-
class='text-blue-500 dark:text-blue-400 hover:underline font-bold'
28-
>
29-
projects
30-
</a>!
24+
<Pill text='projects' href='/projects' size='large' internalLink={true} />!
3125
</p>
3226
</HeroBanner>

src/src/pages/projects/[...slug].astro

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { MarkdownHeading } from 'astro';
66
77
// Import the Astro component for rendering remote markdown
88
import RemoteReadme from '../../components/RemoteReadme.astro';
9+
import ContentTabs from '../../components/ContentTabs.astro';
910
1011
interface Props {
1112
project: CollectionEntry<'projects'>;
@@ -84,16 +85,17 @@ const remoteReadmeUrls: string[] =
8485
>
8586
<Prose>
8687
{
87-
project.data.hasContent ?
88-
<>
89-
<Content />
90-
{remoteReadmeUrls.length > 0 && (
91-
<>
92-
<hr class='my-8 border-gray-200 dark:border-gray-700' />
93-
<RemoteReadme urls={remoteReadmeUrls} sourceUrl={remoteReadmeUrls[0]} />
94-
</>
95-
)}
96-
</>
88+
project.data.hasContent && remoteReadmeUrls.length > 0 ?
89+
<ContentTabs>
90+
<div slot='overview'>
91+
<Content />
92+
</div>
93+
<div slot='readme'>
94+
<RemoteReadme urls={remoteReadmeUrls} sourceUrl={remoteReadmeUrls[0]} />
95+
</div>
96+
</ContentTabs>
97+
: project.data.hasContent ?
98+
<Content />
9799
: remoteReadmeUrls.length > 0 ?
98100
<RemoteReadme urls={remoteReadmeUrls} sourceUrl={remoteReadmeUrls[0]} />
99101
: <div class='text-gray-500 dark:text-gray-400 italic text-center py-8 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-600'>

0 commit comments

Comments
 (0)