@contentrain/astro-kit 0.3.1 → 0.5.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/README.md +2 -0
- package/catalog.json +29 -2
- package/components/faq/Faq.astro +1 -1
- package/components/faq/meta.json +1 -1
- package/components/footer/Footer.astro +4 -3
- package/components/footer/meta.json +1 -1
- package/components/header/Header.astro +15 -6
- package/components/header/meta.json +3 -0
- package/components/nav/Nav.astro +11 -4
- package/components/nav/fixtures.json +1 -0
- package/components/nav/meta.json +8 -0
- package/components/post-card/PostCard.astro +4 -2
- package/components/post-card/meta.json +2 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -66,6 +66,8 @@ theme applies them to the kit's markers, and only when the site sets them:
|
|
|
66
66
|
- `--text-body`: `[data-kit-text]`, the lead and body paragraphs of hero, CTA
|
|
67
67
|
and card grid.
|
|
68
68
|
- `--text-nav`: the header navigation, `[data-cr-part="nav-header"]`.
|
|
69
|
+
- `--text-footer`: the footer's text and links, `[data-kit-footer]` (the kit's
|
|
70
|
+
own size is `text-sm`).
|
|
69
71
|
- `--spacing-section`: the distance between two consecutive sections; each
|
|
70
72
|
`[data-kit-section]` gets half of it as vertical padding, except
|
|
71
73
|
where `data-kit-spacing="none"`.
|
package/catalog.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"--radius-image",
|
|
23
23
|
"--spacing-gutter",
|
|
24
24
|
"--text-nav",
|
|
25
|
+
"--text-footer",
|
|
25
26
|
"--text-heading-1",
|
|
26
27
|
"--text-heading-2",
|
|
27
28
|
"--text-heading-3",
|
|
@@ -563,7 +564,7 @@
|
|
|
563
564
|
"plain"
|
|
564
565
|
],
|
|
565
566
|
"default": "lines",
|
|
566
|
-
"description": "lines: rows divided by rules. cards: each question in a rounded box. plain: the browser's own disclosure triangle without rules or boxes (WordPress's details block)."
|
|
567
|
+
"description": "lines: rows divided by rules. cards: each question in a rounded box. plain: the browser's own disclosure triangle without rules or boxes, questions at the body size (WordPress's details block)."
|
|
567
568
|
},
|
|
568
569
|
"tone": {
|
|
569
570
|
"options": [
|
|
@@ -739,7 +740,7 @@
|
|
|
739
740
|
},
|
|
740
741
|
"copyright": {
|
|
741
742
|
"type": "string",
|
|
742
|
-
"description": "Copyright line;
|
|
743
|
+
"description": "Copyright line; {year} is the current year. Defaults to © <year> <siteName>."
|
|
743
744
|
},
|
|
744
745
|
"navLabel": {
|
|
745
746
|
"type": "string",
|
|
@@ -937,6 +938,19 @@
|
|
|
937
938
|
"required": true,
|
|
938
939
|
"description": "Site title (site.title)."
|
|
939
940
|
},
|
|
941
|
+
"tagline": {
|
|
942
|
+
"type": "string",
|
|
943
|
+
"description": "A line under the site name, as themes that print the tagline in the header do (site.tagline)."
|
|
944
|
+
},
|
|
945
|
+
"brandSize": {
|
|
946
|
+
"type": "string",
|
|
947
|
+
"description": "The site name's size where the source theme sets its own (a CSS length)."
|
|
948
|
+
},
|
|
949
|
+
"linkNav": {
|
|
950
|
+
"type": "boolean",
|
|
951
|
+
"default": false,
|
|
952
|
+
"description": "The navigation in the site's link colour, as themes that colour every link do."
|
|
953
|
+
},
|
|
940
954
|
"logo": {
|
|
941
955
|
"type": "object",
|
|
942
956
|
"fields": {
|
|
@@ -1223,6 +1237,14 @@
|
|
|
1223
1237
|
],
|
|
1224
1238
|
"default": "compact",
|
|
1225
1239
|
"description": "Gap and text size of the inline list."
|
|
1240
|
+
},
|
|
1241
|
+
"color": {
|
|
1242
|
+
"options": [
|
|
1243
|
+
"current",
|
|
1244
|
+
"link"
|
|
1245
|
+
],
|
|
1246
|
+
"default": "current",
|
|
1247
|
+
"description": "current: the surrounding text, dimmed until hovered. link: the site's link colour (--color-link, else the accent) at full strength."
|
|
1226
1248
|
}
|
|
1227
1249
|
},
|
|
1228
1250
|
"props": {
|
|
@@ -1575,6 +1597,11 @@
|
|
|
1575
1597
|
"max": 3,
|
|
1576
1598
|
"default": 2,
|
|
1577
1599
|
"description": "Title heading level in the page outline."
|
|
1600
|
+
},
|
|
1601
|
+
"linkTitle": {
|
|
1602
|
+
"type": "boolean",
|
|
1603
|
+
"default": false,
|
|
1604
|
+
"description": "The title in the site's link colour (--color-link), as themes that colour every link do."
|
|
1578
1605
|
}
|
|
1579
1606
|
},
|
|
1580
1607
|
"slots": {},
|
package/components/faq/Faq.astro
CHANGED
|
@@ -26,7 +26,7 @@ const faq = tv({
|
|
|
26
26
|
lines: { list: 'divide-y divide-line border-y border-line' },
|
|
27
27
|
cards: { list: 'gap-3', item: 'rounded-card border border-line px-5', summary: 'py-5' },
|
|
28
28
|
// WordPress's details block: the browser's own disclosure triangle, no rules or box.
|
|
29
|
-
plain: { list: 'gap-2', summary: 'list-item list-[revert] py-1 text-
|
|
29
|
+
plain: { list: 'gap-2', summary: 'list-item list-[revert] py-1 text-[length:var(--text-body,1rem)] [font-weight:inherit] [&::-webkit-details-marker]:inline', marker: 'hidden', answer: 'pt-2 pl-5' },
|
|
30
30
|
},
|
|
31
31
|
tone: { default: {}, muted: { item: 'bg-surface' }, inverse: { answer: 'prose-invert' }, accent: { answer: 'prose-invert' } },
|
|
32
32
|
},
|
package/components/faq/meta.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"uses": [],
|
|
7
7
|
"shared": ["Section.astro"],
|
|
8
8
|
"variants": {
|
|
9
|
-
"style": { "options": ["lines", "cards", "plain"], "default": "lines", "description": "lines: rows divided by rules. cards: each question in a rounded box. plain: the browser's own disclosure triangle without rules or boxes (WordPress's details block)." },
|
|
9
|
+
"style": { "options": ["lines", "cards", "plain"], "default": "lines", "description": "lines: rows divided by rules. cards: each question in a rounded box. plain: the browser's own disclosure triangle without rules or boxes, questions at the body size (WordPress's details block)." },
|
|
10
10
|
"tone": { "options": ["default", "muted", "inverse", "accent"], "default": "default", "description": "Section background." }
|
|
11
11
|
},
|
|
12
12
|
"props": {
|
|
@@ -33,7 +33,7 @@ interface Props {
|
|
|
33
33
|
/** A single row of links (the footer menu). */
|
|
34
34
|
links?: LinkInput[] | undefined
|
|
35
35
|
social?: LinkInput[] | undefined
|
|
36
|
-
/** The copyright line, e.g. "© 2026 Northwind Studio". */
|
|
36
|
+
/** The copyright line, e.g. "© 2026 Northwind Studio"; `{year}` is the current year. */
|
|
37
37
|
copyright?: string | undefined
|
|
38
38
|
/** Accessible name of the footer navigation ("Footer"). */
|
|
39
39
|
navLabel?: string | undefined
|
|
@@ -43,10 +43,11 @@ interface Props {
|
|
|
43
43
|
|
|
44
44
|
const { siteName, tagline, columns = [], links = [], social = [], copyright, navLabel = 'Footer', layout = 'simple', tone } = Astro.props
|
|
45
45
|
const { root, inner, muted, link } = footer({ layout, tone })
|
|
46
|
-
const
|
|
46
|
+
const year = String(new Date().getFullYear())
|
|
47
|
+
const line = copyright?.replaceAll('{year}', year) ?? `© ${year} ${siteName}`
|
|
47
48
|
---
|
|
48
49
|
<footer class={root()}>
|
|
49
|
-
<div class={inner()} style={layout === 'columns' ? `--footer-columns: ${Math.max(columns.length, 1)}` : undefined}>
|
|
50
|
+
<div class={inner()} data-kit-footer style={layout === 'columns' ? `--footer-columns: ${Math.max(columns.length, 1)}` : undefined}>
|
|
50
51
|
<div class="flex flex-col gap-3">
|
|
51
52
|
<p class="text-base font-semibold" data-kit-brand>{siteName}</p>
|
|
52
53
|
{tagline && <p class={muted()} data-kit-tagline>{tagline}</p>}
|
|
@@ -31,6 +31,12 @@ const header = tv({
|
|
|
31
31
|
|
|
32
32
|
interface Props {
|
|
33
33
|
siteName: string
|
|
34
|
+
/** A line under the site name, as themes that print the site's tagline in the header do. */
|
|
35
|
+
tagline?: string | undefined
|
|
36
|
+
/** The site name's size, where the source theme sets its own (a CSS length). */
|
|
37
|
+
brandSize?: string | undefined
|
|
38
|
+
/** The navigation in the site's link colour, as themes that colour every link do (Hello Elementor). */
|
|
39
|
+
linkNav?: boolean | undefined
|
|
34
40
|
logo?: ImageInput | undefined
|
|
35
41
|
/** Show the site name next to the logo. */
|
|
36
42
|
showName?: boolean | undefined
|
|
@@ -45,17 +51,20 @@ interface Props {
|
|
|
45
51
|
position?: 'static' | 'sticky' | undefined
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
const { siteName, logo, showName = true, homeHref = '/', items, action, navLabel, menuLabel, closeLabel, layout, tone, position } = Astro.props
|
|
54
|
+
const { siteName, tagline, brandSize, linkNav, logo, showName = true, homeHref = '/', items, action, navLabel, menuLabel, closeLabel, layout, tone, position } = Astro.props
|
|
49
55
|
const { root, inner, brand } = header({ layout, tone, position })
|
|
50
56
|
---
|
|
51
57
|
<header class={root()}>
|
|
52
58
|
<div class={inner()}>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
<div class="flex flex-col gap-1">
|
|
60
|
+
<a href={homeHref} class={brand()} aria-label={showName ? undefined : siteName} style={brandSize ? `font-size: ${brandSize}` : undefined} data-kit-brand>
|
|
61
|
+
{logo && <KitImage image={logo} priority class="h-8 w-auto" sizes="160px" />}
|
|
62
|
+
{showName && <span>{siteName}</span>}
|
|
63
|
+
</a>
|
|
64
|
+
{tagline && <p class="opacity-75" data-kit-tagline>{tagline}</p>}
|
|
65
|
+
</div>
|
|
57
66
|
<div class:list={['flex items-center gap-4', layout === 'centered' ? 'justify-center' : 'ml-auto']}>
|
|
58
|
-
<Nav items={items} label={navLabel} menuLabel={menuLabel} closeLabel={closeLabel} part="nav-header" align={layout === 'centered' ? 'center' : 'end'} />
|
|
67
|
+
<Nav items={items} label={navLabel} menuLabel={menuLabel} closeLabel={closeLabel} part="nav-header" color={linkNav ? 'link' : 'current'} align={layout === 'centered' ? 'center' : 'end'} />
|
|
59
68
|
{action && <Button action={action} size="sm" class="hidden md:inline-flex" />}
|
|
60
69
|
</div>
|
|
61
70
|
</div>
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"props": {
|
|
14
14
|
"siteName": { "type": "string", "required": true, "description": "Site title (site.title)." },
|
|
15
|
+
"tagline": { "type": "string", "description": "A line under the site name, as themes that print the tagline in the header do (site.tagline)." },
|
|
16
|
+
"brandSize": { "type": "string", "description": "The site name's size where the source theme sets its own (a CSS length)." },
|
|
17
|
+
"linkNav": { "type": "boolean", "default": false, "description": "The navigation in the site's link colour, as themes that colour every link do." },
|
|
15
18
|
"logo": { "type": "object", "fields": { "src": { "type": "image", "required": true }, "alt": { "type": "string", "required": true }, "width": { "type": "integer" }, "height": { "type": "integer" } }, "description": "Logo image (site.logo)." },
|
|
16
19
|
"showName": { "type": "boolean", "default": true, "description": "Show the site name next to the logo." },
|
|
17
20
|
"homeHref": { "type": "url", "default": "/", "description": "Where the brand links." },
|
package/components/nav/Nav.astro
CHANGED
|
@@ -8,7 +8,7 @@ import type { NavItem } from '../_shared/types'
|
|
|
8
8
|
const nav = tv({
|
|
9
9
|
slots: {
|
|
10
10
|
list: 'hidden items-center md:flex',
|
|
11
|
-
link: '
|
|
11
|
+
link: 'no-underline aria-[current=page]:font-semibold',
|
|
12
12
|
// hidden, not invisible: a closed submenu takes no space, so one under the last item cannot widen the page.
|
|
13
13
|
sub: 'absolute top-full z-30 hidden min-w-48 rounded-lg border border-line bg-surface p-2 text-ink shadow-lg group-hover:block group-focus-within:block',
|
|
14
14
|
toggle: 'inline-flex cursor-pointer list-none items-center gap-2 rounded-md px-3 py-2 text-sm font-medium hover:bg-current/10 md:hidden [&::-webkit-details-marker]:hidden',
|
|
@@ -17,8 +17,14 @@ const nav = tv({
|
|
|
17
17
|
// Submenus open towards the page: leftwards under an end-aligned menu.
|
|
18
18
|
align: { start: { list: 'justify-start', sub: 'left-0' }, end: { list: 'justify-end', sub: 'right-0' }, center: { list: 'justify-center', sub: 'left-0' } },
|
|
19
19
|
density: { compact: { list: 'gap-5 text-sm' }, comfortable: { list: 'gap-8 text-base' } },
|
|
20
|
+
// current: the surrounding text, dimmed until hovered. link: the site's link colour at full strength, as themes that
|
|
21
|
+
// colour every link draw their menu; the toggle keeps the surrounding text.
|
|
22
|
+
color: {
|
|
23
|
+
current: { link: 'text-current opacity-80 hover:opacity-100 aria-[current=page]:opacity-100' },
|
|
24
|
+
link: { link: 'text-[color:var(--color-link,var(--color-accent))]' },
|
|
25
|
+
},
|
|
20
26
|
},
|
|
21
|
-
defaultVariants: { align: 'end', density: 'compact' },
|
|
27
|
+
defaultVariants: { align: 'end', density: 'compact', color: 'current' },
|
|
22
28
|
})
|
|
23
29
|
|
|
24
30
|
interface Props {
|
|
@@ -32,14 +38,15 @@ interface Props {
|
|
|
32
38
|
mobile?: 'popover' | 'details' | undefined
|
|
33
39
|
align?: 'start' | 'end' | 'center' | undefined
|
|
34
40
|
density?: 'compact' | 'comfortable' | undefined
|
|
41
|
+
color?: 'current' | 'link' | undefined
|
|
35
42
|
/** Unique per page when a page has two navigations. */
|
|
36
43
|
id?: string | undefined
|
|
37
44
|
/** What the navigation is, for checks that find a page's parts (`data-cr-part`, e.g. "nav-header"). */
|
|
38
45
|
part?: string | undefined
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
const { items, label = 'Main', menuLabel = 'Menu', closeLabel = 'Close', mobile = 'popover', align, density, id = 'site-nav', part } = Astro.props
|
|
42
|
-
const { list, link, sub, toggle } = nav({ align, density })
|
|
48
|
+
const { items, label = 'Main', menuLabel = 'Menu', closeLabel = 'Close', mobile = 'popover', align, density, color, id = 'site-nav', part } = Astro.props
|
|
49
|
+
const { list, link, sub, toggle } = nav({ align, density, color })
|
|
43
50
|
const current = Astro.url.pathname
|
|
44
51
|
const drawer = `${id}-drawer`
|
|
45
52
|
---
|
package/components/nav/meta.json
CHANGED
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
],
|
|
33
33
|
"default": "compact",
|
|
34
34
|
"description": "Gap and text size of the inline list."
|
|
35
|
+
},
|
|
36
|
+
"color": {
|
|
37
|
+
"options": [
|
|
38
|
+
"current",
|
|
39
|
+
"link"
|
|
40
|
+
],
|
|
41
|
+
"default": "current",
|
|
42
|
+
"description": "current: the surrounding text, dimmed until hovered. link: the site's link colour (--color-link, else the accent) at full strength."
|
|
35
43
|
}
|
|
36
44
|
},
|
|
37
45
|
"props": {
|
|
@@ -37,9 +37,11 @@ interface Props {
|
|
|
37
37
|
author?: string | undefined
|
|
38
38
|
headingLevel?: 2 | 3 | undefined
|
|
39
39
|
layout?: 'vertical' | 'horizontal' | 'minimal' | undefined
|
|
40
|
+
/** The title in the site's link colour, as themes that colour every link do (Hello Elementor). */
|
|
41
|
+
linkTitle?: boolean | undefined
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout } = Astro.props
|
|
44
|
+
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout, linkTitle } = Astro.props
|
|
43
45
|
const date = rawDate === undefined ? undefined : new Date(rawDate)
|
|
44
46
|
const slots = card({ layout })
|
|
45
47
|
const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
@@ -58,7 +60,7 @@ const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
|
58
60
|
{date && <time datetime={date.toISOString()}>{date.toLocaleDateString(locale, { dateStyle: 'long', timeZone: 'UTC' })}</time>}
|
|
59
61
|
</p>
|
|
60
62
|
)}
|
|
61
|
-
<Heading class={slots.title()}>
|
|
63
|
+
<Heading class={slots.title({ class: linkTitle ? 'text-[color:var(--color-link,var(--color-accent))]' : undefined })}>
|
|
62
64
|
<a href={href} class="text-current no-underline after:absolute after:inset-0 after:content-[''] hover:text-accent">{title}</a>
|
|
63
65
|
</Heading>
|
|
64
66
|
{excerpt && <p class={slots.excerpt()}>{excerpt}</p>}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"image": { "type": "object", "fields": { "src": { "type": "image", "required": true }, "alt": { "type": "string", "required": true }, "width": { "type": "integer" }, "height": { "type": "integer" } }, "description": "Cover (posts.cover → media)." },
|
|
18
18
|
"category": { "type": "object", "fields": { "label": { "type": "string", "required": true }, "href": { "type": "url", "required": true } }, "description": "Primary category and its archive." },
|
|
19
19
|
"author": { "type": "string", "description": "Author name (posts.author → authors.name)." },
|
|
20
|
-
"headingLevel": { "type": "integer", "min": 2, "max": 3, "default": 2, "description": "Title heading level in the page outline." }
|
|
20
|
+
"headingLevel": { "type": "integer", "min": 2, "max": 3, "default": 2, "description": "Title heading level in the page outline." },
|
|
21
|
+
"linkTitle": { "type": "boolean", "default": false, "description": "The title in the site's link colour (--color-link), as themes that colour every link do." }
|
|
21
22
|
},
|
|
22
23
|
"slots": {},
|
|
23
24
|
"js": "none",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/astro-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Copy-in Astro components for Contentrain sites (shadcn model): typed props, tailwind-variants, zero JavaScript by default — with a machine-readable catalog",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"mapping"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@contentrain/types": "1.
|
|
45
|
+
"@contentrain/types": "1.29.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.10.0",
|