Skip to content

Commit 97d236a

Browse files
authored
Merge pull request #23 from nexlabstudio/feat/header-footer
feat: support for announcement and footer
2 parents 84a3196 + 380e100 commit 97d236a

File tree

6 files changed

+380
-0
lines changed

6 files changed

+380
-0
lines changed

docs/config/theme.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,30 @@ Use your browser's DevTools to inspect elements and discover classes. Here are t
270270
| `.badge` | Badge component |
271271
| `.tree` | File tree component |
272272

273+
**Footer**
274+
275+
| Class | Element |
276+
|-------|---------|
277+
| `.footer` | Page footer container |
278+
| `.footer-links` | Footer link groups grid |
279+
| `.footer-link-group` | Individual link group column |
280+
| `.footer-link-group-title` | Group heading (e.g. "Resources") |
281+
| `.footer-copyright` | Copyright text |
282+
| `.footer-social` | Social links row |
283+
| `.footer-powered` | "Powered by Stardust" text |
284+
285+
**Announcement**
286+
287+
| Class | Element |
288+
|-------|---------|
289+
| `.announcement` | Announcement bar container |
290+
| `.announcement-info` | Info style (primary color tint) |
291+
| `.announcement-warning` | Warning style (amber tint) |
292+
| `.announcement-success` | Success style (green tint) |
293+
| `.announcement-content` | Announcement text/link |
294+
| `.announcement-dismiss` | Dismiss button |
295+
| `.announcement.dismissed` | Hidden state after dismiss |
296+
273297
**Navigation**
274298

275299
| Class | Element |

lib/src/generator/builders/page_layout_builder.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ class PageLayoutBuilder {
2121

2222
final socialLinks = _buildSocialLinks();
2323
final logoHtml = _buildLogo();
24+
final announcement = _buildAnnouncement();
2425

2526
return '''
27+
$announcement
2628
<header class="header">
2729
<div class="header-inner">
2830
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu">
@@ -65,6 +67,31 @@ class PageLayoutBuilder {
6567
''';
6668
}
6769

70+
String _buildAnnouncement() {
71+
final announcement = config.header.announcement;
72+
if (announcement == null) return '';
73+
74+
final style = announcement.style;
75+
final content = switch (announcement.link) {
76+
final String link => '<a href="$link" class="announcement-content">${announcement.text}</a>',
77+
null => '<span class="announcement-content">${announcement.text}</span>',
78+
};
79+
final dismissBtn = announcement.dismissible
80+
? '''
81+
<button class="announcement-dismiss" aria-label="Dismiss announcement">
82+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
83+
<line x1="18" y1="6" x2="6" y2="18"/>
84+
<line x1="6" y1="6" x2="18" y2="18"/>
85+
</svg>
86+
</button>'''
87+
: '';
88+
89+
return '''
90+
<div class="announcement announcement-$style" id="announcement">
91+
$content$dismissBtn
92+
</div>''';
93+
}
94+
6895
String _buildLogo() {
6996
final logo = config.logo;
7097

@@ -317,9 +344,13 @@ class PageLayoutBuilder {
317344

318345
String buildFooter() {
319346
final footerSocial = _buildFooterSocialLinks();
347+
final footerLinks = _buildFooterLinks();
348+
final copyright = config.footer.copyright;
320349

321350
return '''
322351
<footer class="footer">
352+
$footerLinks
353+
${copyright != null ? '<div class="footer-copyright">$copyright</div>' : ''}
323354
$footerSocial
324355
<div class="footer-powered">
325356
Powered by
@@ -332,6 +363,26 @@ class PageLayoutBuilder {
332363
''';
333364
}
334365

366+
String _buildFooterLinks() {
367+
final links = config.footer.links;
368+
if (links.isEmpty) return '';
369+
370+
final groups = links.map((group) {
371+
final items =
372+
group.items.map((item) => '<li><a href="${item.href}">${item.label}</a></li>').join('\n ');
373+
374+
return '''
375+
<div class="footer-link-group">
376+
<h4 class="footer-link-group-title">${group.group}</h4>
377+
<ul>
378+
$items
379+
</ul>
380+
</div>''';
381+
}).join('\n');
382+
383+
return '<div class="footer-links">\n$groups\n </div>';
384+
}
385+
335386
String _titleCase(String text) => text
336387
.replaceAll('-', ' ')
337388
.replaceAll('_', ' ')

lib/src/generator/builders/page_scripts_builder.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,22 @@ class PageScriptsBuilder {
132132
});
133133
}
134134
135+
const announcement = document.getElementById('announcement');
136+
if (announcement) {
137+
const text = announcement.textContent.trim();
138+
const key = 'stardust-dismiss-' + text.substring(0, 50).replace(/\\s+/g, '-').toLowerCase();
139+
if (localStorage.getItem(key) === 'dismissed') {
140+
announcement.classList.add('dismissed');
141+
}
142+
const dismissBtn = announcement.querySelector('.announcement-dismiss');
143+
if (dismissBtn) {
144+
dismissBtn.addEventListener('click', () => {
145+
announcement.classList.add('dismissed');
146+
localStorage.setItem(key, 'dismissed');
147+
});
148+
}
149+
}
150+
135151
document.querySelectorAll('.code-group').forEach(group => {
136152
const buttons = group.querySelectorAll('.tab-button');
137153
const panels = group.querySelectorAll('.tab-panel');

lib/src/generator/builders/page_styles_builder.dart

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class PageStylesBuilder {
5353
}
5454
5555
${_buildBaseStyles()}
56+
${_buildAnnouncementStyles()}
5657
${_buildHeaderStyles()}
5758
${_buildSearchStyles()}
5859
${_buildLayoutStyles()}
@@ -91,6 +92,59 @@ ${_buildCustomStyles()}
9192
return '\n /* Custom styles */\n $buffer';
9293
}
9394

95+
String _buildAnnouncementStyles() => '''
96+
.announcement {
97+
display: flex;
98+
align-items: center;
99+
justify-content: center;
100+
gap: 0.5rem;
101+
padding: 0.5rem 1rem;
102+
font-size: 0.8125rem;
103+
font-weight: 500;
104+
text-align: center;
105+
}
106+
107+
.announcement-info {
108+
background: color-mix(in srgb, var(--color-primary) 10%, var(--color-bg));
109+
color: var(--color-primary);
110+
}
111+
112+
.announcement-warning {
113+
background: color-mix(in srgb, #f59e0b 10%, var(--color-bg));
114+
color: #b45309;
115+
}
116+
117+
.announcement-success {
118+
background: color-mix(in srgb, #10b981 10%, var(--color-bg));
119+
color: #059669;
120+
}
121+
122+
.announcement-content {
123+
color: inherit;
124+
text-decoration: none;
125+
}
126+
127+
.announcement-content:hover {
128+
text-decoration: underline;
129+
}
130+
131+
.announcement-dismiss {
132+
background: none;
133+
border: none;
134+
color: inherit;
135+
cursor: pointer;
136+
padding: 0.25rem;
137+
opacity: 0.7;
138+
}
139+
140+
.announcement-dismiss:hover {
141+
opacity: 1;
142+
}
143+
144+
.announcement.dismissed {
145+
display: none;
146+
}''';
147+
94148
String _buildBaseStyles() => '''
95149
* {
96150
margin: 0;
@@ -2206,6 +2260,50 @@ ${_buildCustomStyles()}
22062260
22072261
.footer-powered a:hover {
22082262
color: var(--color-primary);
2263+
}
2264+
2265+
.footer-links {
2266+
display: grid;
2267+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
2268+
gap: 2rem;
2269+
width: 100%;
2270+
max-width: 90rem;
2271+
padding: 0 1.5rem;
2272+
margin-bottom: 2rem;
2273+
}
2274+
2275+
.footer-link-group-title {
2276+
font-size: 0.8125rem;
2277+
font-weight: 600;
2278+
text-transform: uppercase;
2279+
letter-spacing: 0.05em;
2280+
color: var(--color-text);
2281+
margin-bottom: 0.75rem;
2282+
}
2283+
2284+
.footer-links ul {
2285+
list-style: none;
2286+
}
2287+
2288+
.footer-links li {
2289+
margin-bottom: 0.375rem;
2290+
}
2291+
2292+
.footer-links a {
2293+
color: var(--color-text-secondary);
2294+
text-decoration: none;
2295+
font-size: 0.875rem;
2296+
transition: color 0.15s;
2297+
}
2298+
2299+
.footer-links a:hover {
2300+
color: var(--color-primary);
2301+
}
2302+
2303+
.footer-copyright {
2304+
font-size: 0.8125rem;
2305+
color: var(--color-text-secondary);
2306+
margin-bottom: 0.75rem;
22092307
}''';
22102308

22112309
String _buildSocialStyles() => '''

stardust.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,35 @@ sidebar:
113113
- slug: deployment/netlify
114114
label: Netlify
115115

116+
header:
117+
announcement:
118+
text: "Stardust v1.0 is here! A Dart-native docs generator with zero config."
119+
link: https://github.com/nexlabstudio/stardust
120+
dismissible: true
121+
style: info
122+
123+
footer:
124+
copyright: "© 2026 Nexlab Studio. All rights reserved."
125+
links:
126+
- group: Resources
127+
items:
128+
- label: Documentation
129+
href: /
130+
- label: Components
131+
href: /components/callouts
132+
- label: Quick Start
133+
href: /quickstart
134+
- group: Community
135+
items:
136+
- label: GitHub
137+
href: https://github.com/nexlabstudio/stardust
138+
- label: Issues
139+
href: https://github.com/nexlabstudio/stardust/issues
140+
- group: More
141+
items:
142+
- label: CLI Reference
143+
href: /cli/init
144+
116145
theme:
117146
colors:
118147
primary: "#6366f1"

0 commit comments

Comments
 (0)