@contentrain/astro-kit 0.23.5 → 0.24.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/catalog.json +6 -1
- package/components/contact-form/ContactForm.astro +2 -3
- package/components/contact-form/contact-form.ts +5 -4
- package/components/footer/Footer.astro +6 -3
- package/components/footer/fixtures.json +2 -1
- package/components/footer/footer.ts +34 -0
- package/components/footer/meta.json +4 -0
- package/package.json +2 -2
package/catalog.json
CHANGED
|
@@ -1280,6 +1280,10 @@
|
|
|
1280
1280
|
"type": "string",
|
|
1281
1281
|
"description": "Font size of the footer's links, a CSS length such as 17.88px; absent, the footer's own."
|
|
1282
1282
|
},
|
|
1283
|
+
"linkSizeNarrow": {
|
|
1284
|
+
"type": "string",
|
|
1285
|
+
"description": "Font size of the footer's links below the md breakpoint, a CSS length such as 16.4px; absent, linkSize holds at every width."
|
|
1286
|
+
},
|
|
1283
1287
|
"border": {
|
|
1284
1288
|
"type": "boolean",
|
|
1285
1289
|
"content": false,
|
|
@@ -1308,7 +1312,8 @@
|
|
|
1308
1312
|
},
|
|
1309
1313
|
"a11y": "One <footer> landmark; each link group is a <nav> named by its title. Social links open in a new tab with rel=me.",
|
|
1310
1314
|
"files": [
|
|
1311
|
-
"Footer.astro"
|
|
1315
|
+
"Footer.astro",
|
|
1316
|
+
"footer.ts"
|
|
1312
1317
|
]
|
|
1313
1318
|
},
|
|
1314
1319
|
{
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
// StudioForm): its fields, spam protection and notifications live in Studio,
|
|
5
5
|
// and submissions arrive there for review — no form plugin, no mail server.
|
|
6
6
|
// Until the site is bound to a Studio project the form renders nothing and the
|
|
7
|
-
// contact details still show
|
|
8
|
-
// drawn (a heading over a form that is not there). When the owner chose another home for forms at
|
|
7
|
+
// contact details still show, and so do the section's heading and introduction (only the form goes). When the owner chose another home for forms at
|
|
9
8
|
// migration (site.config.ts `features.forms`), the same place holds a form
|
|
10
9
|
// that posts to their form service, an address to write to, or a link to the
|
|
11
10
|
// form on WordPress.
|
|
@@ -71,7 +70,7 @@ interface Props {
|
|
|
71
70
|
const { model, heading, intro, formHeading, details = [], layout, align, tone, id = 'contact' } = Astro.props
|
|
72
71
|
// Nothing to render (Studio not bound yet): a split would leave an empty column — the details stand alone.
|
|
73
72
|
const formShown = formHome(model) !== null
|
|
74
|
-
const bare = contactSectionBare({ formShown, intro, details })
|
|
73
|
+
const bare = contactSectionBare({ formShown, heading, intro, details })
|
|
75
74
|
const s = contact({ layout: formShown ? layout : 'stacked', align, tone })
|
|
76
75
|
const headingId = heading ? `${id}-title` : undefined
|
|
77
76
|
// A description list needs a term for every detail; an icon list carried from the source has values only.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// When the contact section has nothing to show. Its form is drawn only where a home for it exists (Studio bound, the
|
|
2
2
|
// WordPress page known, a service address that is https); until then the section stands on its heading, introduction
|
|
3
|
-
// and details.
|
|
4
|
-
//
|
|
3
|
+
// and details. The source's words are never dropped with the form: a page whose contact section holds only a heading
|
|
4
|
+
// ("Bize yazın") keeps it, because the text is the site's own and a fidelity gate compares it. Only a section with no
|
|
5
|
+
// heading, introduction or details at all — nothing to print — is left out, and appears with its form once it has a home.
|
|
5
6
|
|
|
6
|
-
export function contactSectionBare(input: { formShown: boolean, intro?: string | undefined, details?: readonly unknown[] | undefined }): boolean {
|
|
7
|
-
return !input.formShown && !input.intro && !(input.details?.length ?? 0)
|
|
7
|
+
export function contactSectionBare(input: { formShown: boolean, heading?: string | undefined, intro?: string | undefined, details?: readonly unknown[] | undefined }): boolean {
|
|
8
|
+
return !input.formShown && !input.heading && !input.intro && !(input.details?.length ?? 0)
|
|
8
9
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// links and the copyright line.
|
|
4
4
|
import { tv } from 'tailwind-variants'
|
|
5
5
|
import type { LinkInput } from '../_shared/types'
|
|
6
|
+
import { footerLinkSizeClasses, footerVars } from './footer'
|
|
6
7
|
|
|
7
8
|
const footer = tv({
|
|
8
9
|
slots: {
|
|
@@ -67,14 +68,16 @@ interface Props {
|
|
|
67
68
|
packGapNarrow?: string | undefined
|
|
68
69
|
/** The links' font size, a CSS length (`17.88px`); absent, the footer's own. */
|
|
69
70
|
linkSize?: string | undefined
|
|
71
|
+
/** The links' font size below `md`, a CSS length; absent, `linkSize` holds at every width. */
|
|
72
|
+
linkSizeNarrow?: string | undefined
|
|
70
73
|
/** A border on the footer's top side: absent, the default tone has one and the others do not. */
|
|
71
74
|
border?: boolean | undefined
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
const { siteName, tagline, columns = [], links = [], social = [], contact, copyright, consentLabel, navLabel = 'Footer', layout = 'simple', tone, align, pack, packGap, narrow, packGapNarrow, linkSize, border } = Astro.props
|
|
77
|
+
const { siteName, tagline, columns = [], links = [], social = [], contact, copyright, consentLabel, navLabel = 'Footer', layout = 'simple', tone, align, pack, packGap, narrow, packGapNarrow, linkSize, linkSizeNarrow, border } = Astro.props
|
|
75
78
|
const { root, inner, muted, link } = footer({ layout, tone, align, pack: layout === 'columns' ? pack : undefined, narrow: layout === 'columns' && pack === 'end' ? narrow : undefined, border: (border ?? (tone ?? 'default') === 'default') ? 'on' : 'off' })
|
|
76
79
|
// Custom properties the packed columns and sized links read; none when none is set.
|
|
77
|
-
const vars =
|
|
80
|
+
const vars = footerVars({ layout, columnCount: columns.length, pack, narrow, packGap, packGapNarrow, linkSize, linkSizeNarrow })
|
|
78
81
|
const year = String(new Date().getFullYear())
|
|
79
82
|
const phoneHref = contact?.phone?.replace(/[^\d+]/g, '')
|
|
80
83
|
const hasContact = Boolean(contact?.address || contact?.phone || contact?.email)
|
|
@@ -84,7 +87,7 @@ const hasBottom = Boolean(line || consentLabel)
|
|
|
84
87
|
---
|
|
85
88
|
<!-- The cookie settings control is a button, not a link: the Consent component's listener reopens its bar on any [data-kit-consent-open]. -->
|
|
86
89
|
<footer class={root()}>
|
|
87
|
-
<div class:list={[inner(), linkSize
|
|
90
|
+
<div class:list={[inner(), ...footerLinkSizeClasses({ linkSize, linkSizeNarrow })]} data-kit-footer style={vars || undefined}>
|
|
88
91
|
<div class="flex flex-col gap-3">
|
|
89
92
|
<p class="text-base font-semibold" data-kit-brand>{siteName}</p>
|
|
90
93
|
{tagline && <p class={muted()} data-kit-tagline>{tagline}</p>}
|
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
{ "name": "centered", "props": { "siteName": "Golden Agency", "tagline": "Websites for makers", "align": "center", "copyright": "© 2026 Golden Agency", "links": [{ "label": "Services", "href": "/services/" }, { "label": "Contact", "href": "/contact/" }], "social": [{ "label": "Instagram", "href": "https://instagram.com/" }] } },
|
|
6
6
|
{ "name": "consent-settings", "props": { "siteName": "Golden Agency", "copyright": "© 2026 Golden Agency", "consentLabel": "Cookie settings", "links": [{ "label": "Privacy", "href": "/privacy/" }] } },
|
|
7
7
|
{ "name": "columns-end", "props": { "siteName": "Golden Works", "tagline": "Websites for makers", "layout": "columns", "pack": "end", "packGap": "128px", "linkSize": "17.88px", "copyright": "© 2026 Golden Works", "columns": [{ "label": "Studio", "links": [{ "label": "About", "href": "/about/" }, { "label": "Work", "href": "/work/" }] }, { "label": "Contact", "links": [{ "label": "Email", "href": "mailto:hello@example.com" }, { "label": "Press", "href": "/press/" }] }] } },
|
|
8
|
-
{ "name": "columns-end-row", "props": { "siteName": "Golden Works", "layout": "columns", "pack": "end", "narrow": "row", "packGap": "128px", "packGapNarrow": "48px", "linkSize": "17.88px", "copyright": "© 2026 Golden Works", "columns": [{ "label": "Studio", "links": [{ "label": "About", "href": "/about/" }, { "label": "Work", "href": "/work/" }] }, { "label": "Contact", "links": [{ "label": "Email", "href": "mailto:hello@example.com" }] }] } }
|
|
8
|
+
{ "name": "columns-end-row", "props": { "siteName": "Golden Works", "layout": "columns", "pack": "end", "narrow": "row", "packGap": "128px", "packGapNarrow": "48px", "linkSize": "17.88px", "copyright": "© 2026 Golden Works", "columns": [{ "label": "Studio", "links": [{ "label": "About", "href": "/about/" }, { "label": "Work", "href": "/work/" }] }, { "label": "Contact", "links": [{ "label": "Email", "href": "mailto:hello@example.com" }] }] } },
|
|
9
|
+
{ "name": "columns-end-row-narrow-size", "props": { "siteName": "Golden Works", "layout": "columns", "pack": "end", "narrow": "row", "packGap": "128px", "packGapNarrow": "48px", "linkSize": "17.88px", "linkSizeNarrow": "16.4px", "copyright": "© 2026 Golden Works", "columns": [{ "label": "Studio", "links": [{ "label": "About", "href": "/about/" }, { "label": "Work", "href": "/work/" }] }, { "label": "Contact", "links": [{ "label": "Email", "href": "mailto:hello@example.com" }] }] } }
|
|
9
10
|
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface FooterVarsInput {
|
|
2
|
+
layout: 'simple' | 'columns'
|
|
3
|
+
columnCount: number
|
|
4
|
+
pack?: 'spread' | 'end' | undefined
|
|
5
|
+
narrow?: 'stack' | 'row' | undefined
|
|
6
|
+
packGap?: string | undefined
|
|
7
|
+
packGapNarrow?: string | undefined
|
|
8
|
+
linkSize?: string | undefined
|
|
9
|
+
linkSizeNarrow?: string | undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The custom properties the packed columns and sized links read, as one `style` value (empty: none). `linkSizeNarrow` is
|
|
14
|
+
* its own property, applied below `md` by the class below whether the columns stack or stay in a row.
|
|
15
|
+
*/
|
|
16
|
+
export function footerVars(i: FooterVarsInput): string {
|
|
17
|
+
const columns = i.layout === 'columns'
|
|
18
|
+
const packed = columns && i.pack === 'end'
|
|
19
|
+
return [
|
|
20
|
+
columns ? `--footer-columns: ${Math.max(i.columnCount, 1)}` : '',
|
|
21
|
+
packed && i.packGap ? `--footer-gap: ${i.packGap}` : '',
|
|
22
|
+
packed && i.narrow === 'row' && i.packGapNarrow ? `--footer-gap-narrow: ${i.packGapNarrow}` : '',
|
|
23
|
+
i.linkSize ? `--footer-link-size: ${i.linkSize}` : '',
|
|
24
|
+
i.linkSizeNarrow ? `--footer-link-size-narrow: ${i.linkSizeNarrow}` : '',
|
|
25
|
+
].filter(Boolean).join('; ')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The link-size classes: the wide size everywhere, the narrow one (when given) overriding it below `md`. */
|
|
29
|
+
export function footerLinkSizeClasses(i: Pick<FooterVarsInput, 'linkSize' | 'linkSizeNarrow'>): string[] {
|
|
30
|
+
return [
|
|
31
|
+
i.linkSize ? '[&_nav_a]:text-[length:var(--footer-link-size)]' : '',
|
|
32
|
+
i.linkSizeNarrow ? 'max-md:[&_nav_a]:text-[length:var(--footer-link-size-narrow)]' : '',
|
|
33
|
+
].filter(Boolean)
|
|
34
|
+
}
|
|
@@ -148,6 +148,10 @@
|
|
|
148
148
|
"type": "string",
|
|
149
149
|
"description": "Font size of the footer's links, a CSS length such as 17.88px; absent, the footer's own."
|
|
150
150
|
},
|
|
151
|
+
"linkSizeNarrow": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"description": "Font size of the footer's links below the md breakpoint, a CSS length such as 16.4px; absent, linkSize holds at every width."
|
|
154
|
+
},
|
|
151
155
|
"border": {
|
|
152
156
|
"type": "boolean",
|
|
153
157
|
"content": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/astro-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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.48.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.10.0",
|