stack-site-builder 1.19.4 → 1.20.0
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/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/index.mjs +12 -0
- package/package.json +1 -1
- package/src/components/CardsHome.astro +6 -2
- package/src/components/DefaultFooter.astro +26 -0
- package/src/components/ProductLanding.astro +1 -1
- package/src/components/ProductsIndex.astro +1 -1
- package/src/content.ts +3 -0
- package/src/layouts/BaseLayout.astro +5 -15
- package/src/lib/home.ts +4 -0
- package/src/styles/global.css +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,26 @@ content schema, while a consuming site supplies only content, taxonomy data and
|
|
|
11
11
|
config. Sites track the theme with `pnpm up stack-site-builder`, so each release
|
|
12
12
|
here is a plain version bump they pull in.
|
|
13
13
|
|
|
14
|
+
## [1.20.0] - 2026-07-24
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Per-site custom footer** — footers are site identity, not theme chrome.
|
|
19
|
+
A site that ships `src/components/Footer.astro` (receiving a `lang` prop)
|
|
20
|
+
now replaces the theme's stock footer wholesale; without one, the stock
|
|
21
|
+
"Built with Astro" footer renders as before (now living in
|
|
22
|
+
`DefaultFooter.astro`, resolved through the `@aas-footer` alias).
|
|
23
|
+
|
|
24
|
+
## [1.19.5] - 2026-07-24
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`iconInvert` for dark mode** — black line-art icons (e.g. Tabler-style
|
|
29
|
+
SVGs) vanished on the dark card background. Home cards and the home hero
|
|
30
|
+
(`iconInvert: true` in `site.home`), and product entries (`iconInvert`
|
|
31
|
+
frontmatter — applies on the products index and the landing hero) can now
|
|
32
|
+
flag an icon to be color-inverted when the dark theme is active.
|
|
33
|
+
|
|
14
34
|
## [1.19.4] - 2026-07-24
|
|
15
35
|
|
|
16
36
|
### Fixed
|
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ export const collections = defineAasCollections({ categoryMap });
|
|
|
43
43
|
| `src/content/{stacks,concepts,courses,products,papers,articles,slides}/` | The content, one MDX file per locale |
|
|
44
44
|
| `src/content/pages/` | Standalone top-level pages (e.g. an About/소개), rendered at `/<slug>/` and optionally linked in the header nav |
|
|
45
45
|
| `public/` · `samples/` | Logos/favicons and runnable sample projects |
|
|
46
|
+
| `src/components/Footer.astro` (optional) | Replaces the theme's stock footer wholesale — receives a `lang` prop |
|
|
46
47
|
|
|
47
48
|
The theme reaches the site's data through the `@aas-data/*` alias (set up by
|
|
48
49
|
the integration), so everything above is swappable per site.
|
package/index.mjs
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// components resolve per-site taxonomy (categories, glossary, site identity)
|
|
16
16
|
// at build time.
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
18
19
|
import mdx from '@astrojs/mdx';
|
|
19
20
|
import sitemap from '@astrojs/sitemap';
|
|
20
21
|
import tailwindcss from '@tailwindcss/vite';
|
|
@@ -175,6 +176,17 @@ export default function aasTheme({ glossary, sections = {} }) {
|
|
|
175
176
|
alias: {
|
|
176
177
|
'@aas-data': fileURLToPath(new URL('./src/data', config.root)),
|
|
177
178
|
'@assets': fileURLToPath(new URL('./src/assets', config.root)),
|
|
179
|
+
// Footers are site identity, not theme chrome: when the site
|
|
180
|
+
// ships `src/components/Footer.astro`, BaseLayout renders it
|
|
181
|
+
// (via this alias) instead of the theme's stock footer.
|
|
182
|
+
'@aas-footer': (() => {
|
|
183
|
+
const siteFooter = fileURLToPath(
|
|
184
|
+
new URL('./src/components/Footer.astro', config.root),
|
|
185
|
+
);
|
|
186
|
+
return existsSync(siteFooter)
|
|
187
|
+
? siteFooter
|
|
188
|
+
: fileURLToPath(new URL('./src/components/DefaultFooter.astro', import.meta.url));
|
|
189
|
+
})(),
|
|
178
190
|
},
|
|
179
191
|
},
|
|
180
192
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stack-site-builder",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.20.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The engine behind the awesome-*-stack catalog sites: an Astro theme with the catalog/concepts/articles/slides/samples routes, components, styles and markdown pipeline. Sites provide content, taxonomy data and config.",
|
|
7
7
|
"repository": {
|
|
@@ -29,7 +29,7 @@ const resolveHref = (href: string, external?: boolean) =>
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
<section class="py-14 text-center">
|
|
32
|
-
{h?.hero?.icon && <img src={withBase(h.hero.icon)} alt="" class=
|
|
32
|
+
{h?.hero?.icon && <img src={withBase(h.hero.icon)} alt="" class:list={['mx-auto h-20 w-20 rounded-[22%] object-contain', h.hero.iconInvert && 'aas-icon-invert']} />}
|
|
33
33
|
<h1 class="mt-5 text-4xl font-bold tracking-tight sm:text-5xl">{heroTitle}</h1>
|
|
34
34
|
<p class="mx-auto mt-4 max-w-2xl text-xl text-[var(--aas-muted)]" set:html={heroSubtitle} />
|
|
35
35
|
</section>
|
|
@@ -50,7 +50,11 @@ const resolveHref = (href: string, external?: boolean) =>
|
|
|
50
50
|
<img
|
|
51
51
|
src={withBase(c.icon)}
|
|
52
52
|
alt=""
|
|
53
|
-
class:list={[
|
|
53
|
+
class:list={[
|
|
54
|
+
'h-20 w-20 shrink-0 object-contain',
|
|
55
|
+
c.rounded ? 'rounded-[22%]' : 'rounded-xl',
|
|
56
|
+
c.iconInvert && 'aas-icon-invert',
|
|
57
|
+
]}
|
|
54
58
|
loading="lazy"
|
|
55
59
|
/>
|
|
56
60
|
)}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
// The theme's stock footer. A site replaces it wholesale by shipping its own
|
|
3
|
+
// `src/components/Footer.astro` — the integration aliases `@aas-footer` to
|
|
4
|
+
// that file when it exists (this one otherwise), so BaseLayout needs no
|
|
5
|
+
// site-specific knowledge. A custom footer receives the same `lang` prop.
|
|
6
|
+
import { useTranslations, type Lang } from '../i18n/ui';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
lang: Lang;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { lang } = Astro.props;
|
|
13
|
+
const t = useTranslations(lang);
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<footer class="border-t border-[var(--aas-border)] py-8 text-center text-sm text-[var(--aas-muted)]">
|
|
17
|
+
<p>
|
|
18
|
+
{t('footer.builtWith')}
|
|
19
|
+
<a
|
|
20
|
+
href="https://astro.build"
|
|
21
|
+
target="_blank"
|
|
22
|
+
rel="noopener noreferrer"
|
|
23
|
+
class="text-[var(--aas-accent)]">Astro</a
|
|
24
|
+
>. {t('footer.contribute')}
|
|
25
|
+
</p>
|
|
26
|
+
</footer>
|
|
@@ -47,7 +47,7 @@ const altBg = (i: number) => i % 2 === 1;
|
|
|
47
47
|
]}
|
|
48
48
|
>
|
|
49
49
|
<div class="mx-auto max-w-[75rem] px-5">
|
|
50
|
-
{d.icon && <img src={withBase(d.icon)} alt="" class=
|
|
50
|
+
{d.icon && <img src={withBase(d.icon)} alt="" class:list={['mx-auto h-24 w-24 rounded-[22%] object-contain', d.iconInvert && 'aas-icon-invert']} />}
|
|
51
51
|
<h1 class="mt-6 text-4xl font-bold tracking-tight sm:text-5xl">{d.title}</h1>
|
|
52
52
|
{d.subtitle && <p class="mt-3 text-xl text-[var(--aas-muted)]" set:html={d.subtitle} />}
|
|
53
53
|
{
|
|
@@ -54,7 +54,7 @@ const sections = productTree.roots
|
|
|
54
54
|
class="aas-lift flex items-center gap-6 rounded-3xl border border-[var(--aas-border)] bg-[var(--aas-panel)] p-6 no-underline"
|
|
55
55
|
>
|
|
56
56
|
{a.data.icon && (
|
|
57
|
-
<img src={withBase(a.data.icon)} alt="" class=
|
|
57
|
+
<img src={withBase(a.data.icon)} alt="" class:list={['h-20 w-20 shrink-0 rounded-[22%] object-contain', a.data.iconInvert && 'aas-icon-invert']} loading="lazy" />
|
|
58
58
|
)}
|
|
59
59
|
<span class="min-w-0">
|
|
60
60
|
<span class="block text-xl font-semibold text-[var(--aas-text)]">{a.data.title}</span>
|
package/src/content.ts
CHANGED
|
@@ -430,6 +430,9 @@ export function defineAasCollections({
|
|
|
430
430
|
// ---- hero ----
|
|
431
431
|
subtitle: z.string().optional(),
|
|
432
432
|
icon: z.string().optional(), // app icon (public/ path)
|
|
433
|
+
// Invert the icon in dark mode — for black line-art icons that would
|
|
434
|
+
// otherwise vanish on the dark card background.
|
|
435
|
+
iconInvert: z.boolean().default(false),
|
|
433
436
|
// Free-form key a site can target from its CSS (`.aas-hero-bg-<key>`)
|
|
434
437
|
// for a custom hero background.
|
|
435
438
|
heroBg: z.string().optional(),
|
|
@@ -6,6 +6,10 @@ import { useTranslations, languages, type Lang } from '../i18n/ui';
|
|
|
6
6
|
import LanguageSwitcher from '../components/LanguageSwitcher.astro';
|
|
7
7
|
import ThemeToggle from '../components/ThemeToggle.astro';
|
|
8
8
|
import BackToTop from '../components/BackToTop.astro';
|
|
9
|
+
// Resolved by the integration: the site's src/components/Footer.astro when it
|
|
10
|
+
// exists, the theme's DefaultFooter otherwise.
|
|
11
|
+
// @ts-expect-error -- virtual alias, no static types
|
|
12
|
+
import Footer from '@aas-footer';
|
|
9
13
|
import { getNavPages, pageSlugOf } from '../lib/pages';
|
|
10
14
|
import { getNavProducts, getTopProducts, productSlugOf } from '../lib/products';
|
|
11
15
|
import { homeTemplate } from '../lib/home';
|
|
@@ -378,21 +382,7 @@ const navItems = allNavItems.filter((item) => !item.section || sectionEnabled(it
|
|
|
378
382
|
<slot />
|
|
379
383
|
</main>
|
|
380
384
|
|
|
381
|
-
<
|
|
382
|
-
class="border-t border-[var(--aas-border)] py-8 text-center text-sm text-[var(--aas-muted)]"
|
|
383
|
-
>
|
|
384
|
-
<p>
|
|
385
|
-
{t('footer.builtWith')}
|
|
386
|
-
<a
|
|
387
|
-
href="https://astro.build"
|
|
388
|
-
target="_blank"
|
|
389
|
-
rel="noopener noreferrer"
|
|
390
|
-
class="text-[var(--aas-accent)]">Astro</a
|
|
391
|
-
>. {
|
|
392
|
-
t('footer.contribute')
|
|
393
|
-
}
|
|
394
|
-
</p>
|
|
395
|
-
</footer>
|
|
385
|
+
<Footer lang={lang} />
|
|
396
386
|
|
|
397
387
|
<BackToTop lang={lang} />
|
|
398
388
|
|
package/src/lib/home.ts
CHANGED
|
@@ -24,6 +24,9 @@ export interface HomeCard {
|
|
|
24
24
|
description?: Localized;
|
|
25
25
|
icon?: string; // public/ path
|
|
26
26
|
rounded?: boolean; // app-icon style rounding for the icon
|
|
27
|
+
/** Invert the icon in dark mode — for black line-art icons that would
|
|
28
|
+
* otherwise vanish on the dark card background. */
|
|
29
|
+
iconInvert?: boolean;
|
|
27
30
|
tags?: string[] | Record<string, string[]>;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -31,6 +34,7 @@ export interface HomeConfig {
|
|
|
31
34
|
template: 'cards';
|
|
32
35
|
hero?: {
|
|
33
36
|
icon?: string; // public/ path, shown above the title
|
|
37
|
+
iconInvert?: boolean; // invert the icon in dark mode (black line art)
|
|
34
38
|
title?: Localized; // defaults to site.name
|
|
35
39
|
subtitle?: Localized; // defaults to the site.tagline UI string
|
|
36
40
|
};
|
package/src/styles/global.css
CHANGED
|
@@ -179,6 +179,12 @@ html {
|
|
|
179
179
|
:root[data-theme='dark'] .aas-deprecated {
|
|
180
180
|
color: #fbbf24;
|
|
181
181
|
}
|
|
182
|
+
/* Icons flagged `iconInvert` (home cards, products) are black line art that
|
|
183
|
+
would vanish on the dark card background — flip them to white in dark. */
|
|
184
|
+
:root[data-theme='dark'] .aas-icon-invert {
|
|
185
|
+
filter: invert(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
182
188
|
/* "No updates in 1+ year" — same amber warning weight as deprecated. */
|
|
183
189
|
.aas-stale {
|
|
184
190
|
background: color-mix(in srgb, #d97706 16%, transparent);
|