@levino/shipyard-base 0.8.4 → 0.8.5

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.
@@ -37,6 +37,8 @@ type Props = {
37
37
  canonicalUrl?: string
38
38
  customMetaTags?: CustomMetaTag[]
39
39
  sidebarNavigation?: NavigationTree
40
+ /** Open Graph object type (`website` or `article`). @default 'website' */
41
+ ogType?: string
40
42
  }
41
43
  } & {
42
44
  title?: string
@@ -46,6 +48,8 @@ type Props = {
46
48
  canonicalUrl?: string
47
49
  customMetaTags?: CustomMetaTag[]
48
50
  sidebarNavigation?: NavigationTree
51
+ /** Open Graph object type (`website` or `article`). @default 'website' */
52
+ ogType?: string
49
53
  }
50
54
 
51
55
  const currentPath = Astro.url.pathname
@@ -110,6 +114,15 @@ const navigation = applyLocaleAndSetActive(config.navigation)
110
114
  const title = getTitle(config.title, props.title)
111
115
  const hasSidebar = !!props.sidebarNavigation
112
116
 
117
+ // Language for the <html> element: current locale on i18n sites, otherwise the
118
+ // configured default locale (omitted entirely when i18n is disabled).
119
+ const htmlLang = Astro.currentLocale ?? (i18n ? i18n.defaultLocale : undefined)
120
+
121
+ // Absolute URL of the current page (no query string), used for og:url and as
122
+ // the self-referential canonical when no explicit canonicalUrl is provided.
123
+ const pageUrl = Astro.site ? new URL(currentPath, Astro.site).href : undefined
124
+ const canonicalUrl = props.canonicalUrl ?? pageUrl
125
+
113
126
  // Helper function to render script attributes
114
127
  const renderScriptAttributes = (script: Script) => {
115
128
  if (typeof script === 'string') {
@@ -119,19 +132,27 @@ const renderScriptAttributes = (script: Script) => {
119
132
  }
120
133
  ---
121
134
 
122
- <html>
135
+ <html lang={htmlLang}>
123
136
  <head>
124
137
  <meta charset="utf-8" />
125
138
  <title>
126
139
  {title}
127
140
  </title>
128
141
  <meta name="viewport" content="width=device-width, initial-scale=1" />
129
- <meta name="description" content={props.description} />
142
+ {props.description && (
143
+ <meta name="description" content={props.description} />
144
+ )}
130
145
  {props.keywords && props.keywords.length > 0 && (
131
146
  <meta name="keywords" content={props.keywords.join(', ')} />
132
147
  )}
148
+ {canonicalUrl && <link rel="canonical" href={canonicalUrl} />}
133
149
  <meta property="og:title" content={title} />
134
- <meta property="og:description" content={props.description} />
150
+ {props.description && (
151
+ <meta property="og:description" content={props.description} />
152
+ )}
153
+ <meta property="og:type" content={props.ogType ?? 'website'} />
154
+ <meta property="og:site_name" content={config.title} />
155
+ {pageUrl && <meta property="og:url" content={pageUrl} />}
135
156
  {ogImageData && ogImageUrl && (
136
157
  <>
137
158
  <meta property="og:image" content={ogImageUrl} />
@@ -147,16 +168,21 @@ const renderScriptAttributes = (script: Script) => {
147
168
  {ogImageAlt && (
148
169
  <meta property="og:image:alt" content={ogImageAlt} />
149
170
  )}
150
- <meta name="twitter:card" content="summary_large_image" />
171
+ </>
172
+ )}
173
+ <meta name="twitter:card" content={ogImageUrl ? 'summary_large_image' : 'summary'} />
174
+ <meta name="twitter:title" content={title} />
175
+ {props.description && (
176
+ <meta name="twitter:description" content={props.description} />
177
+ )}
178
+ {ogImageUrl && (
179
+ <>
151
180
  <meta name="twitter:image" content={ogImageUrl} />
152
181
  {ogImageAlt && (
153
182
  <meta name="twitter:image:alt" content={ogImageAlt} />
154
183
  )}
155
184
  </>
156
185
  )}
157
- {props.canonicalUrl && (
158
- <link rel="canonical" href={props.canonicalUrl} />
159
- )}
160
186
  {props.customMetaTags?.map((tag) => (
161
187
  <meta
162
188
  {...(tag.name && { name: tag.name })}
@@ -172,6 +198,12 @@ const renderScriptAttributes = (script: Script) => {
172
198
  <slot name="head" />
173
199
  </head>
174
200
  <body class="flex min-h-screen flex-col">
201
+ <a
202
+ href="#main-content"
203
+ class="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 btn btn-primary btn-sm"
204
+ >
205
+ {config.skipToContentLabel ?? 'Skip to content'}
206
+ </a>
175
207
  {config.announcementBar && <AnnouncementBar config={config.announcementBar} />}
176
208
  <div class={cn("drawer grow", { "lg:drawer-open": hasSidebar })}>
177
209
  <input id="drawer" type="checkbox" class="drawer-toggle" />
@@ -184,7 +216,7 @@ const renderScriptAttributes = (script: Script) => {
184
216
  <slot name="navbarExtra" slot="navbarExtra" />
185
217
  </GlobalDesktopNavigation>
186
218
  <div class="grow">
187
- <div class="mx-auto max-w-7xl px-4 pb-12">
219
+ <div id="main-content" class="mx-auto max-w-7xl px-4 pb-12">
188
220
  <slot />
189
221
  </div>
190
222
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levino/shipyard-base",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Core layouts, components, and configuration for shipyard - a composable page builder for Astro",
5
5
  "keywords": [
6
6
  "astro",
@@ -221,6 +221,12 @@ export interface Config {
221
221
  * @example 'https://example.com/og.jpg'
222
222
  */
223
223
  defaultImage?: string
224
+ /**
225
+ * Label for the accessibility "skip to content" link, rendered as the first
226
+ * focusable element on every page. Set this to localize it.
227
+ * @default 'Skip to content'
228
+ */
229
+ skipToContentLabel?: string
224
230
  /**
225
231
  * Path to your app's CSS entry point.
226
232
  * This file should set up Tailwind CSS with @source directives for shipyard packages.