@lettercamp/cli 0.1.10 → 0.1.11
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/dist/build.js +2 -1
- package/dist/code-block.js +8 -8
- package/dist/content-component-syntax.js +75 -0
- package/dist/content-components.js +222 -0
- package/dist/dev.js +19 -3
- package/dist/media-dimensions.js +4 -3
- package/dist/publication-links.js +7 -0
- package/dist/text-styles.js +23 -0
- package/package.json +7 -1
- package/renderer/src/components/AuthorByline.astro +9 -2
- package/renderer/src/components/Header.astro +1 -1
- package/renderer/src/components/SocialLink.astro +1 -1
- package/renderer/src/layouts/SiteLayout.astro +7 -3
- package/renderer/src/lib/block-widths.ts +0 -6
- package/renderer/src/lib/button.js +11 -9
- package/renderer/src/lib/code-block.ts +8 -8
- package/renderer/src/lib/font-catalog.js +1650 -0
- package/renderer/src/lib/font-fallbacks.js +36 -0
- package/renderer/src/lib/fonts.js +31 -8
- package/renderer/src/lib/image-widths.ts +0 -3
- package/renderer/src/lib/key-takeaways.ts +25 -0
- package/renderer/src/lib/post-share.ts +10 -0
- package/renderer/src/lib/site-colors.ts +42 -0
- package/renderer/src/lib/table-styles.ts +3 -3
- package/renderer/src/pages/[...slug].astro +3 -0
- package/renderer/src/styles/post-share.css +44 -27
- package/renderer/src/styles/site.css +72 -57
|
@@ -193,4 +193,40 @@ export const fontFallbacks = {
|
|
|
193
193
|
{ "weight": 600, "local": ["Arial Bold", "Arial-BoldMT"], "sizeAdjust": "97.2956%", "ascentOverride": "101.1351%", "descentOverride": "32.4783%", "lineGapOverride": "0.0000%" },
|
|
194
194
|
{ "weight": 700, "local": ["Arial Bold", "Arial-BoldMT"], "sizeAdjust": "99.5874%", "ascentOverride": "98.8077%", "descentOverride": "31.7309%", "lineGapOverride": "0.0000%" },
|
|
195
195
|
] },
|
|
196
|
+
"Geist Mono": { generic: "monospace", faces: [
|
|
197
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
|
|
198
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
|
|
199
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
|
|
200
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
|
|
201
|
+
] },
|
|
202
|
+
"JetBrains Mono": { generic: "monospace", faces: [
|
|
203
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
|
|
204
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
|
|
205
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
|
|
206
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
|
|
207
|
+
] },
|
|
208
|
+
"Fira Code": { generic: "monospace", faces: [
|
|
209
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
|
|
210
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
|
|
211
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
|
|
212
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
|
|
213
|
+
] },
|
|
214
|
+
"Roboto Mono": { generic: "monospace", faces: [
|
|
215
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
|
|
216
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
|
|
217
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
|
|
218
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
|
|
219
|
+
] },
|
|
220
|
+
"IBM Plex Mono": { generic: "monospace", faces: [
|
|
221
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
|
|
222
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
|
|
223
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
|
|
224
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
|
|
225
|
+
] },
|
|
226
|
+
"Source Code Pro": { generic: "monospace", faces: [
|
|
227
|
+
{ "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
|
|
228
|
+
{ "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
|
|
229
|
+
{ "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
|
|
230
|
+
{ "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
|
|
231
|
+
] },
|
|
196
232
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Generated from lettercamp/lib/font-resources.ts.
|
|
2
2
|
import { fontFallbacks } from "./font-fallbacks.js";
|
|
3
|
-
|
|
3
|
+
import { fontCatalog } from "./font-catalog.js";
|
|
4
|
+
export function fontResources(typography = {}, headingWeight = 500) {
|
|
4
5
|
const quote = (value) => '"' + Array.from(String(value), char => /[a-zA-Z0-9 -]/.test(char) ? char : '\\' + char.codePointAt(0).toString(16) + ' ').join('') + '"';
|
|
5
6
|
const google = new Set();
|
|
7
|
+
const resolved = new Set();
|
|
8
|
+
const preloads = new Set();
|
|
6
9
|
const custom = new Map();
|
|
7
10
|
const faces = [];
|
|
8
|
-
const resolve = (input) => {
|
|
11
|
+
const resolve = (input, criticalWeight) => {
|
|
9
12
|
const value = input && typeof input === "object" ? input : undefined;
|
|
10
13
|
if (value && typeof value.url === 'string' && !(typeof value.google === 'string' && value.google.trim())) {
|
|
11
14
|
try {
|
|
@@ -24,25 +27,45 @@ export function fontResources(typography = {}) {
|
|
|
24
27
|
catch { /* Invalid custom URLs use the default family. */ }
|
|
25
28
|
}
|
|
26
29
|
const family = value && typeof value.google === 'string' && value.google.trim() ? value.google.trim() : 'Inter';
|
|
30
|
+
const catalog = Object.hasOwn(fontCatalog, family) ? fontCatalog[family] : undefined;
|
|
31
|
+
if (catalog) {
|
|
32
|
+
if (!resolved.has(family))
|
|
33
|
+
for (const face of catalog.faces) {
|
|
34
|
+
faces.push('@font-face{font-family:' + quote(family) + ';src:url(' + quote(face.url) + ') format("woff2");font-weight:' + face.weight
|
|
35
|
+
+ ';font-style:normal;font-display:swap;unicode-range:' + face.unicodeRange + ';}');
|
|
36
|
+
}
|
|
37
|
+
// Prioritize only the title and body Latin files. Other subsets load on demand.
|
|
38
|
+
const distance = (weight) => {
|
|
39
|
+
const [min, max = min] = weight.split(' ').map(Number);
|
|
40
|
+
return Math.max(min - criticalWeight, criticalWeight - max, 0);
|
|
41
|
+
};
|
|
42
|
+
const critical = catalog.faces.filter(face => face.subset === 'latin').sort((a, b) => distance(a.weight) - distance(b.weight))[0];
|
|
43
|
+
if (critical)
|
|
44
|
+
preloads.add(critical.url);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Preserve support for existing CLI settings naming fonts outside our picker.
|
|
48
|
+
google.add(family);
|
|
49
|
+
}
|
|
27
50
|
const fallback = Object.hasOwn(fontFallbacks, family) ? fontFallbacks[family] : undefined;
|
|
28
51
|
if (fallback) {
|
|
29
52
|
const alias = family + ' Fallback';
|
|
30
|
-
if (!
|
|
53
|
+
if (!resolved.has(family))
|
|
31
54
|
for (const face of fallback.faces) {
|
|
32
55
|
faces.push('@font-face{font-family:' + quote(alias) + ';src:' + face.local.map(name => 'local(' + quote(name) + ')').join(',')
|
|
33
56
|
+ ';font-weight:' + face.weight + ';font-style:normal;size-adjust:' + face.sizeAdjust
|
|
34
57
|
+ ';ascent-override:' + face.ascentOverride + ';descent-override:' + face.descentOverride + ';line-gap-override:' + face.lineGapOverride + ';}');
|
|
35
58
|
}
|
|
36
|
-
|
|
59
|
+
resolved.add(family);
|
|
37
60
|
return quote(family) + ', ' + quote(alias) + ', ' + fallback.generic;
|
|
38
61
|
}
|
|
39
|
-
|
|
62
|
+
resolved.add(family);
|
|
40
63
|
return quote(family) + ', Arial, Helvetica, sans-serif';
|
|
41
64
|
};
|
|
42
|
-
const headingFamily = resolve(typography.headingFont);
|
|
43
|
-
const bodyFamily = resolve(typography.bodyFont);
|
|
65
|
+
const headingFamily = resolve(typography.headingFont, headingWeight);
|
|
66
|
+
const bodyFamily = resolve(typography.bodyFont, 400);
|
|
44
67
|
return {
|
|
45
|
-
headingFamily, bodyFamily, css: faces.join('\n'),
|
|
68
|
+
headingFamily, bodyFamily, css: faces.join('\n'), preloads: [...preloads],
|
|
46
69
|
googleUrl: google.size ? 'https://fonts.googleapis.com/css2?' + [...google].map(family => 'family=' + encodeURIComponent(family) + ':wght@' + (Object.hasOwn(fontFallbacks, family) ? fontFallbacks[family].faces.map(face => face.weight).join(';') : '400;500;600;700')).join('&') + '&display=swap' : null,
|
|
47
70
|
};
|
|
48
71
|
}
|
|
@@ -7,7 +7,4 @@ export const imageWidthStyles = `
|
|
|
7
7
|
.publication-post .article-hero-image:not([data-image-width="regular"]):not([data-image-width="full"]) { width: min(100%, calc(var(--post-content-width) + 128px)); max-width: none; }
|
|
8
8
|
.publication-post .article-hero-image[data-image-width="full"] { width: 100%; max-width: none; }
|
|
9
9
|
.publication-post .article-hero-image[data-image-width="regular"] { width: min(100%, var(--cover-image-width, var(--post-content-width)), var(--post-content-width)); max-width: none; }
|
|
10
|
-
.publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) { display: block; }
|
|
11
|
-
.publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) .article-intro { max-width: var(--post-content-width); margin-inline: auto; }
|
|
12
|
-
.publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) .article-hero-image { margin-top: 32px; }
|
|
13
10
|
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Generated from lettercamp/lib/key-takeaways.ts.
|
|
2
|
+
// Shared by the page editor, draft preview, and published renderer.
|
|
3
|
+
export const keyTakeawaysStyles = `
|
|
4
|
+
[data-lettercamp-key-takeaways] {
|
|
5
|
+
box-sizing: border-box; margin-block: 24px; padding: 20px 24px;
|
|
6
|
+
border-radius: 8px; background: var(--site-surface, #fafafa); color: inherit;
|
|
7
|
+
font: inherit; overflow-wrap: anywhere;
|
|
8
|
+
}
|
|
9
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :is(h1, h2, h3, h4, h5, h6) {
|
|
10
|
+
margin: 0 0 10px; padding: 0;
|
|
11
|
+
}
|
|
12
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] :is(ul, ol) {
|
|
13
|
+
margin: 0; padding: 0 0 0 1.4em;
|
|
14
|
+
}
|
|
15
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] li {
|
|
16
|
+
margin: 6px 0; padding: 0 0 0 .2em;
|
|
17
|
+
}
|
|
18
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] p { margin: 0; padding: 0; min-height: 0; }
|
|
19
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > p:first-child { margin-bottom: 10px; }
|
|
20
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :last-child,
|
|
21
|
+
[data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :last-child > li:last-child { margin-bottom: 0; }
|
|
22
|
+
@media (max-width: 600px) {
|
|
23
|
+
[data-lettercamp-key-takeaways] { padding: 16px 18px; }
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
@@ -31,6 +31,16 @@ export const postShareScript = `
|
|
|
31
31
|
const ensureTrigger = () => {
|
|
32
32
|
const byline = document.querySelector('.publication-post .byline');
|
|
33
33
|
if (byline && !byline.querySelector('.post-share-button')) byline.insertAdjacentHTML('beforeend', triggerMarkup);
|
|
34
|
+
const intro = document.querySelector('.publication-post .article-intro');
|
|
35
|
+
if (!intro) return;
|
|
36
|
+
let row = intro.querySelector('.article-meta-row');
|
|
37
|
+
if (!row) {
|
|
38
|
+
row = document.createElement('div'); row.className = 'article-meta-row';
|
|
39
|
+
const category = intro.querySelector('.article-category');
|
|
40
|
+
if (category) row.append(category);
|
|
41
|
+
intro.prepend(row);
|
|
42
|
+
}
|
|
43
|
+
if (!row.querySelector('.post-share-button')) row.insertAdjacentHTML('beforeend', triggerMarkup);
|
|
34
44
|
};
|
|
35
45
|
const action = (tag, name, label, graphic) => '<' + tag + ' class="post-share-action" data-share="' + name + '"' + (tag === 'button' ? ' type="button"' : '') + '><span class="post-share-action-icon">' + graphic + '</span><span data-label>' + label + '</span></' + tag + '>';
|
|
36
46
|
const external = (link, href) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Generated from lettercamp/lib/site-colors.ts.
|
|
2
|
+
// Shared by published sites and live previews. Keep this function self-contained:
|
|
3
|
+
// the preview bridge embeds it in the publication iframe.
|
|
4
|
+
export function siteColors(design?: { backgroundColor?: unknown; accentColor?: unknown }): Record<string, string> {
|
|
5
|
+
const [background, accent] = [design?.backgroundColor, design?.accentColor].map((value, index) =>
|
|
6
|
+
typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value) ? value.toLowerCase() : index === 0 ? "#ffffff" : "");
|
|
7
|
+
const [text, accentText] = [background, accent || background].map(color => {
|
|
8
|
+
const linear = [1, 3, 5].map(offset => {
|
|
9
|
+
const channel = parseInt(color.slice(offset, offset + 2), 16) / 255;
|
|
10
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
11
|
+
});
|
|
12
|
+
const luminance = linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722;
|
|
13
|
+
return (luminance + 0.05) / 0.05 >= 1.05 / (luminance + 0.05) ? "#000000" : "#ffffff";
|
|
14
|
+
});
|
|
15
|
+
const base = [1, 3, 5].map(offset => parseInt(background.slice(offset, offset + 2), 16));
|
|
16
|
+
const target = text === "#000000" ? 0 : 255;
|
|
17
|
+
// Reference grays on white. Retain each shade's distance along the
|
|
18
|
+
// background-to-foreground scale; e.g. 250 preserves #fafafa on white.
|
|
19
|
+
const shades: Record<string, number> = {
|
|
20
|
+
"surface": 250,
|
|
21
|
+
"surface-hover": 245,
|
|
22
|
+
"surface-active": 238,
|
|
23
|
+
"border": 240,
|
|
24
|
+
"border-strong": 221,
|
|
25
|
+
"text-strong": 65,
|
|
26
|
+
"text-secondary": 111,
|
|
27
|
+
"text-muted": 134,
|
|
28
|
+
"text-faint": 180,
|
|
29
|
+
};
|
|
30
|
+
const colors: Record<string, string> = {
|
|
31
|
+
"color-scheme": target === 255 ? "dark" : "light",
|
|
32
|
+
"--site-background": background,
|
|
33
|
+
"--site-foreground": text,
|
|
34
|
+
"--site-accent": accent || "inherit",
|
|
35
|
+
"--site-accent-text": accent ? accentText : "inherit",
|
|
36
|
+
"--site-code-tint": target === 255 ? "45%" : "0%",
|
|
37
|
+
};
|
|
38
|
+
for (const [name, gray] of Object.entries(shades)) {
|
|
39
|
+
colors[`--site-${name}`] = "#" + base.map(channel => Math.round(channel + (target - channel) * (1 - gray / 255)).toString(16).padStart(2, "0")).join("");
|
|
40
|
+
}
|
|
41
|
+
return colors;
|
|
42
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated from lettercamp/lib/table-styles.ts.
|
|
2
2
|
// Shared by the editable table, safe preview, and published renderer.
|
|
3
3
|
export const tableStyles = `
|
|
4
|
-
[data-lettercamp-table] { position: relative; margin-block: 24px 32px; color: #303034; font: inherit; }
|
|
4
|
+
[data-lettercamp-table] { position: relative; margin-block: 24px 32px; color: var(--site-text-strong, #303034); font: inherit; }
|
|
5
5
|
[data-lettercamp-table] > table { width: 100%; border-collapse: collapse; table-layout: fixed; border-spacing: 0; font: inherit; color: inherit; }
|
|
6
|
-
[data-lettercamp-table] > table > tbody > tr > :is(td, th) { position: relative; border: 1px solid #f0f0f0; padding: 0; min-width: 0; vertical-align: top; font: inherit; text-align: left; }
|
|
6
|
+
[data-lettercamp-table] > table > tbody > tr > :is(td, th) { position: relative; border: 1px solid var(--site-border, #f0f0f0); padding: 0; min-width: 0; vertical-align: top; font: inherit; text-align: left; }
|
|
7
7
|
[data-lettercamp-table] [data-table-content] { display: block; box-sizing: border-box; min-height: calc(1.8em + 16px); padding: 8px 12px; line-height: inherit; overflow-wrap: anywhere; white-space: pre-wrap; outline: none; }
|
|
8
8
|
[data-lettercamp-table] [data-table-cell-height] [data-table-content] { height: var(--table-cell-height); min-height: 0; overflow: auto; }
|
|
9
9
|
[data-lettercamp-table] :is(td, th) > p, [data-lettercamp-table] [data-table-content] > p { margin: 0; padding: 0; min-height: 0; }
|
|
10
|
-
[data-lettercamp-table] [data-table-content] code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; padding: .18em .35em; background: #f3f3f3; border-radius: 3px; }
|
|
10
|
+
[data-lettercamp-table] [data-table-content] code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; padding: .18em .35em; background: var(--site-surface-hover, #f3f3f3); border-radius: 3px; }
|
|
11
11
|
[data-lettercamp-table] [data-table-content] a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
|
|
12
12
|
`;
|
|
@@ -35,9 +35,12 @@ const coverWidth = /^[0-9]+$/.test(String(frontmatter.coverWidth)) && Number(fro
|
|
|
35
35
|
data-post-cover={postLayout.showCover === false ? 'hidden' : 'visible'} data-post-toc={postLayout.showTableOfContents === false ? 'hidden' : 'visible'}>
|
|
36
36
|
<header class:list={['article-header', 'article-hero', { 'article-hero-with-image': coverImage }]}>
|
|
37
37
|
<div class="article-intro">
|
|
38
|
+
<div class="article-meta-row">
|
|
38
39
|
{category && <p class="article-category">
|
|
39
40
|
<a href={categoryHref}>{category}</a>
|
|
40
41
|
</p>}
|
|
42
|
+
<Fragment set:html={postShareButton} />
|
|
43
|
+
</div>
|
|
41
44
|
<h1>{frontmatter.title ?? site.name}</h1>
|
|
42
45
|
{frontmatter.description && <p class="description">{frontmatter.description}</p>}
|
|
43
46
|
<div class="byline">
|
|
@@ -1,56 +1,73 @@
|
|
|
1
1
|
.byline:has(.post-share-button) { display: grid; grid-template-columns: minmax(0, 1fr) auto; width: 100%; }
|
|
2
2
|
.byline:has(.post-share-button) .post-byline { grid-column: 1; grid-row: 1; min-width: 0; }
|
|
3
3
|
.byline > .post-share-button { grid-column: 2; grid-row: 1; justify-self: end; }
|
|
4
|
-
.
|
|
5
|
-
.
|
|
4
|
+
.article-meta-row { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
|
|
5
|
+
.article-meta-row:not(:has(.article-category)) { display: none; }
|
|
6
|
+
.article-meta-row .article-category { min-width: 0; margin: 0; }
|
|
7
|
+
.article-meta-row > .post-share-button { display: none; }
|
|
8
|
+
.post-share-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; flex-shrink: 0; margin-left: auto; padding: 5px 11px; border: 0; border-radius: 999px; background: var(--site-surface-hover); color: var(--site-text-strong); font-size: 13px; line-height: 1.4; }
|
|
9
|
+
.post-share-button:hover { background: var(--site-surface-active); color: var(--site-foreground); }
|
|
6
10
|
.post-share-button svg { width: 15px; height: 15px; }
|
|
7
11
|
html:has(.post-share-dialog[open]) { overflow: hidden; }
|
|
8
|
-
.post-share-dialog { position: fixed; width: min(380px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); margin: auto; padding: 16px; overflow: auto; overscroll-behavior: contain; border: 0; border-radius: 14px; background: var(--site-background, #fff); color:
|
|
12
|
+
.post-share-dialog { position: fixed; width: min(380px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); margin: auto; padding: 16px; overflow: auto; overscroll-behavior: contain; border: 0; border-radius: 14px; background: var(--site-background, #fff); color: var(--site-foreground); box-shadow: 0 20px 80px #0003; font: 13px/1.5 var(--font-family); }
|
|
9
13
|
.post-share-dialog::backdrop { background: #0006; }
|
|
10
14
|
.post-share-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
|
|
11
15
|
.post-share-heading h2 { margin: 0; font: 600 15px/1.4 var(--font-family); letter-spacing: -.3px; }
|
|
12
|
-
.post-share-close { display: grid; place-items: center; flex-shrink: 0; width: 28px; height: 28px; margin: -4px -4px -4px 0; padding: 0; border: 0; background: transparent; color:
|
|
13
|
-
.post-share-close:hover { color:
|
|
16
|
+
.post-share-close { display: grid; place-items: center; flex-shrink: 0; width: 28px; height: 28px; margin: -4px -4px -4px 0; padding: 0; border: 0; background: transparent; color: var(--site-text-secondary); }
|
|
17
|
+
.post-share-close:hover { color: var(--site-foreground); }
|
|
14
18
|
.post-share-close svg { width: 19px; height: 19px; }
|
|
15
|
-
.post-share-preview { overflow: hidden; border: 1px solid
|
|
19
|
+
.post-share-preview { overflow: hidden; border: 1px solid var(--site-border); border-radius: 8px; background: var(--site-surface-hover); color: var(--site-foreground); font-family: Arial, Helvetica, sans-serif; }
|
|
16
20
|
.post-share-image { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; }
|
|
17
21
|
.post-share-preview-copy { padding: 9px 10px; }
|
|
18
|
-
.post-share-domain { margin: 0 0 4px; color:
|
|
22
|
+
.post-share-domain { margin: 0 0 4px; color: var(--site-text-secondary); font-size: 10px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
19
23
|
.post-share-preview h3 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 0; font: 600 14px/1.3 Arial, Helvetica, sans-serif; letter-spacing: 0; text-wrap: auto; }
|
|
20
|
-
.post-share-description { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 4px 0 0; color:
|
|
24
|
+
.post-share-description { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 4px 0 0; color: var(--site-text-secondary); font-size: 11px; line-height: 1.4; overflow-wrap: anywhere; }
|
|
21
25
|
.post-share-description:empty { display: none; }
|
|
22
26
|
.post-share-actions { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin-top: 22px; }
|
|
23
|
-
.post-share-action { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; padding: 0; border: 0; background: transparent; color:
|
|
24
|
-
.post-share-action-icon { display: grid; place-items: center; width: 100%; height: 40px; border-radius: 10px; background:
|
|
25
|
-
.post-share-action:hover, .post-share-action[aria-expanded='true'] { color:
|
|
26
|
-
.post-share-action:hover .post-share-action-icon, .post-share-action[aria-expanded='true'] .post-share-action-icon { background:
|
|
27
|
+
.post-share-action { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; padding: 0; border: 0; background: transparent; color: var(--site-text-strong); font-size: 11px; line-height: 1.4; text-decoration: none; }
|
|
28
|
+
.post-share-action-icon { display: grid; place-items: center; width: 100%; height: 40px; border-radius: 10px; background: var(--site-surface); }
|
|
29
|
+
.post-share-action:hover, .post-share-action[aria-expanded='true'] { color: var(--site-foreground); }
|
|
30
|
+
.post-share-action:hover .post-share-action-icon, .post-share-action[aria-expanded='true'] .post-share-action-icon { background: var(--site-surface-hover); }
|
|
27
31
|
.post-share-action svg { width: 18px; height: 18px; }
|
|
28
|
-
.post-share-menu { position: fixed; inset: auto; width: 184px; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px); margin: 0; padding: 6px; overflow: auto; overscroll-behavior: contain; border: 1px solid
|
|
32
|
+
.post-share-menu { position: fixed; inset: auto; width: 184px; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px); margin: 0; padding: 6px; overflow: auto; overscroll-behavior: contain; border: 1px solid var(--site-border); border-radius: 10px; background: var(--site-surface); color: var(--site-text-strong); box-shadow: 0 6px 24px #0002; font: 12px/1.4 var(--font-family); }
|
|
29
33
|
.post-share-menu::backdrop { background: transparent; }
|
|
30
34
|
.post-share-menu-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 7px 9px; border: 0; border-radius: 6px; background: transparent; color: inherit; text-align: left; font: inherit; text-decoration: none; }
|
|
31
|
-
.post-share-menu-item:is(:hover, :focus-visible) { background:
|
|
32
|
-
.post-share-menu-item svg { width: 15px; height: 15px; color:
|
|
33
|
-
.post-share-menu-item[data-share='native'] { margin-top: 4px; border-top: 1px solid
|
|
34
|
-
.post-share-summaries { margin-top: 14px; padding-top: 12px; border-top: 1px solid
|
|
35
|
+
.post-share-menu-item:is(:hover, :focus-visible) { background: var(--site-surface-active); color: var(--site-foreground); outline: none; }
|
|
36
|
+
.post-share-menu-item svg { width: 15px; height: 15px; color: var(--site-text-muted); }
|
|
37
|
+
.post-share-menu-item[data-share='native'] { margin-top: 4px; border-top: 1px solid var(--site-border); border-radius: 0; }
|
|
38
|
+
.post-share-summaries { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--site-border); }
|
|
35
39
|
.post-share-summaries h3 { margin: 0; font: 600 13px/1.5 var(--font-family); }
|
|
36
|
-
.post-share-summaries > p { margin: 3px 0 9px; color:
|
|
40
|
+
.post-share-summaries > p { margin: 3px 0 9px; color: var(--site-text-secondary); font-size: 12px; }
|
|
37
41
|
.post-share-providers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
|
|
38
|
-
.post-share-provider { display: flex; align-items: center; justify-content: space-between; gap: 5px; padding: 7px 9px; border-radius: 7px; background:
|
|
39
|
-
.post-share-provider:hover { background:
|
|
40
|
-
.post-share-provider svg { width: 13px; height: 13px; flex-shrink: 0; color:
|
|
41
|
-
.post-share-status { margin: 12px 0 0; color:
|
|
42
|
+
.post-share-provider { display: flex; align-items: center; justify-content: space-between; gap: 5px; padding: 7px 9px; border-radius: 7px; background: var(--site-surface); font-size: 12px; text-decoration: none; }
|
|
43
|
+
.post-share-provider:hover { background: var(--site-surface-hover); }
|
|
44
|
+
.post-share-provider svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--site-text-muted); }
|
|
45
|
+
.post-share-status { margin: 12px 0 0; color: var(--site-text-secondary); font-size: 12px; text-align: center; }
|
|
42
46
|
.post-share-status:empty { display: none; }
|
|
43
47
|
.post-share-copy-fallback { margin-top: 12px; }
|
|
44
|
-
.post-share-copy-fallback label { display: block; margin-bottom: 4px; color:
|
|
45
|
-
.post-share-copy-fallback input { width: 100%; min-width: 0; padding: 8px 10px; border: 1px solid
|
|
46
|
-
@media (max-width:
|
|
47
|
-
.
|
|
48
|
-
.
|
|
48
|
+
.post-share-copy-fallback label { display: block; margin-bottom: 4px; color: var(--site-text-secondary); font-size: 12px; }
|
|
49
|
+
.post-share-copy-fallback input { width: 100%; min-width: 0; padding: 8px 10px; border: 1px solid var(--site-border-strong); border-radius: 8px; background: var(--site-background); color: var(--site-text-strong); font: inherit; }
|
|
50
|
+
@media (max-width: 600px) {
|
|
51
|
+
.article-meta-row, .article-meta-row:not(:has(.article-category)) { display: flex; }
|
|
52
|
+
.article-meta-row > .post-share-button { display: inline-flex; }
|
|
53
|
+
.byline:has(.post-share-button) { grid-template-columns: minmax(0, 1fr); }
|
|
54
|
+
.byline > .post-share-button { display: none; }
|
|
55
|
+
.byline .post-byline { flex-wrap: wrap; gap: 6px 16px; overflow: visible; white-space: normal; }
|
|
56
|
+
.byline .post-authors { order: -1; flex: 0 0 100%; min-width: 0; white-space: nowrap; }
|
|
49
57
|
}
|
|
50
58
|
@media (max-width: 400px) {
|
|
51
59
|
.post-share-provider { padding: 7px; font-size: 11px; }
|
|
52
60
|
.byline:has(.post-share-button) { column-gap: 10px; }
|
|
53
61
|
}
|
|
62
|
+
/* Split headers keep metadata above authors, with sharing centered beside both rows. */
|
|
63
|
+
.publication-post[data-post-hero='split'] .byline { display: grid; grid-template-columns: max-content minmax(0, 1fr) auto; gap: 6px 16px; }
|
|
64
|
+
.publication-post[data-post-hero='split'] .byline .post-byline { display: contents; }
|
|
65
|
+
.publication-post[data-post-hero='split'] .byline .post-byline > :is(time, .reading-time) { grid-row: 1; }
|
|
66
|
+
.publication-post[data-post-hero='split'] .byline > .post-share-button { display: inline-flex; grid-column: 3; grid-row: 1 / 3; align-self: center; }
|
|
67
|
+
.publication-post[data-post-hero='split'] .byline .post-authors { grid-column: 1 / 3; grid-row: 2; min-width: 0; order: 0; }
|
|
68
|
+
.publication-post[data-post-hero='split'] .byline .author-names { flex: 1; min-width: 0; white-space: normal; }
|
|
69
|
+
.publication-post[data-post-hero='split'] .article-meta-row > .post-share-button { display: none; }
|
|
70
|
+
.publication-post[data-post-hero='split'] .article-meta-row:not(:has(.article-category)) { display: none; }
|
|
54
71
|
@media (prefers-reduced-motion: no-preference) {
|
|
55
72
|
.post-share-dialog[open] { animation: post-share-enter 140ms ease-out; }
|
|
56
73
|
@keyframes post-share-enter { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }
|