A calm place to write long-form, and publish it to the open social web.
skypress.blog/
1---
2import '../styles/global.css';
3import { buildMetaTags } from '../lib/seo/meta';
4
5interface Props {
6 title: string;
7 description?: string;
8 /**
9 * Optional landing-page sky phase. Rendered as `data-phase` on `<html>` so the
10 * no-JS / pre-paint default has a sky; the inline head script then overwrites it
11 * from the visitor's clock. `<html>` is the single phase carrier — see index.astro.
12 */
13 phase?: string;
14 /** Share image path (resolved to an absolute URL). Defaults to the shared OG image. */
15 image?: string;
16 /** Open Graph object type. Defaults to "website". */
17 ogType?: string;
18 /** Alt text for the share image. */
19 imageAlt?: string;
20 /**
21 * Intrinsic share-image dimensions. Defaulted to 1200x630 only for the
22 * built-in default image; a caller passing a custom image (e.g. a square
23 * publication logo) leaves these unset so we don't mis-declare dimensions.
24 */
25 imageWidth?: number;
26 imageHeight?: number;
27 /**
28 * Whether Base emits the canonical link + Open Graph/Twitter tags. Defaults
29 * to true. Pages that manage their own head tags (e.g. the article page,
30 * which sets article-specific `og:*` + canonical) opt out to avoid emitting
31 * duplicate, conflicting tags.
32 */
33 socialMeta?: boolean;
34}
35const {
36 title,
37 description,
38 phase,
39 image = '/og-default.png',
40 ogType = 'website',
41 imageAlt = 'SkyPress — a writing studio for the open social web',
42 imageWidth,
43 imageHeight,
44 socialMeta = true,
45} = Astro.props;
46
47// Absolute URLs for OG/Twitter, resolved against the configured public origin
48// (astro.config.mjs `site`). Astro.url.pathname drops any query string so the
49// canonical URL stays stable.
50const canonicalUrl = new URL( Astro.url.pathname, Astro.site ).href;
51const imageUrl = new URL( image, Astro.site ).href;
52const isDefaultImage = image === '/og-default.png';
53const ogImageWidth = imageWidth ?? ( isDefaultImage ? 1200 : undefined );
54const ogImageHeight = imageHeight ?? ( isDefaultImage ? 630 : undefined );
55const metaTags = buildMetaTags( {
56 title,
57 description,
58 url: canonicalUrl,
59 image: imageUrl,
60 siteName: 'SkyPress',
61 type: ogType,
62 imageAlt,
63 imageWidth: ogImageWidth,
64 imageHeight: ogImageHeight,
65} );
66---
67
68<!doctype html>
69<html lang="en" data-phase={phase}>
70 <head>
71 <meta charset="utf-8" />
72 <meta name="viewport" content="width=device-width, initial-scale=1" />
73 <meta name="generator" content={Astro.generator} />
74 <link rel="icon" href="/skypress-logo.svg" type="image/svg+xml" />
75 {description && <meta name="description" content={description} />}
76 {
77 socialMeta && (
78 <>
79 <link rel="canonical" href={canonicalUrl} />
80 {metaTags.map( ( tag ) =>
81 tag.property ? (
82 <meta property={tag.property} content={tag.content} />
83 ) : (
84 <meta name={tag.name} content={tag.content} />
85 )
86 )}
87 </>
88 )
89 }
90 <title>{title}</title>
91 <slot name="head" />
92 </head>
93 <body>
94 <slot />
95 </body>
96</html>