tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
handle header sizes and base size for fonts
awarm.space
1 week ago
980fcd7d
3e831c1f
+51
-97
5 changed files
expand all
collapse all
unified
split
app
lish
[did]
[publication]
[rkey]
PostContent.tsx
StaticPostContent.tsx
components
Blocks
TextBlock
index.tsx
src
fonts.ts
utils
blockTextSize.ts
+6
-4
app/lish/[did]/[publication]/[rkey]/PostContent.tsx
···
34
import { PublishedPollBlock } from "./Blocks/PublishedPollBlock";
35
import { PollData } from "./fetchPollData";
36
import { ButtonPrimary } from "components/Buttons";
0
37
38
export function PostContent({
39
blocks,
···
346
case PubLeafletBlocksText.isMain(b.block):
347
return (
348
<p
0
349
className={`textBlock ${className} ${b.block.textSize === "small" ? "text-sm text-secondary" : b.block.textSize === "large" ? "text-lg" : ""}`}
350
{...blockProps}
351
>
···
362
case PubLeafletBlocksHeader.isMain(b.block): {
363
if (b.block.level === 1)
364
return (
365
-
<h2 className={`h1Block ${className}`} {...blockProps}>
366
<TextBlock
367
{...b.block}
368
index={index}
···
373
);
374
if (b.block.level === 2)
375
return (
376
-
<h3 className={`h2Block ${className}`} {...blockProps}>
377
<TextBlock
378
{...b.block}
379
index={index}
···
384
);
385
if (b.block.level === 3)
386
return (
387
-
<h4 className={`h3Block ${className}`} {...blockProps}>
388
<TextBlock
389
{...b.block}
390
index={index}
···
396
// if (b.block.level === 4) return <h4>{b.block.plaintext}</h4>;
397
// if (b.block.level === 5) return <h5>{b.block.plaintext}</h5>;
398
return (
399
-
<h6 className={`h6Block ${className}`} {...blockProps}>
400
<TextBlock
401
{...b.block}
402
index={index}
···
34
import { PublishedPollBlock } from "./Blocks/PublishedPollBlock";
35
import { PollData } from "./fetchPollData";
36
import { ButtonPrimary } from "components/Buttons";
37
+
import { blockTextSize } from "src/utils/blockTextSize";
38
39
export function PostContent({
40
blocks,
···
347
case PubLeafletBlocksText.isMain(b.block):
348
return (
349
<p
350
+
style={{ fontSize: blockTextSize.p }}
351
className={`textBlock ${className} ${b.block.textSize === "small" ? "text-sm text-secondary" : b.block.textSize === "large" ? "text-lg" : ""}`}
352
{...blockProps}
353
>
···
364
case PubLeafletBlocksHeader.isMain(b.block): {
365
if (b.block.level === 1)
366
return (
367
+
<h2 style={{ fontSize: blockTextSize.h1 }} className={`h1Block ${className}`} {...blockProps}>
368
<TextBlock
369
{...b.block}
370
index={index}
···
375
);
376
if (b.block.level === 2)
377
return (
378
+
<h3 style={{ fontSize: blockTextSize.h2 }} className={`h2Block ${className}`} {...blockProps}>
379
<TextBlock
380
{...b.block}
381
index={index}
···
386
);
387
if (b.block.level === 3)
388
return (
389
+
<h4 style={{ fontSize: blockTextSize.h3 }} className={`h3Block ${className}`} {...blockProps}>
390
<TextBlock
391
{...b.block}
392
index={index}
···
398
// if (b.block.level === 4) return <h4>{b.block.plaintext}</h4>;
399
// if (b.block.level === 5) return <h5>{b.block.plaintext}</h5>;
400
return (
401
+
<h6 style={{ fontSize: blockTextSize.h4 }} className={`h6Block ${className}`} {...blockProps}>
402
<TextBlock
403
{...b.block}
404
index={index}
+6
-5
app/lish/[did]/[publication]/[rkey]/StaticPostContent.tsx
···
12
PubLeafletPagesLinearDocument,
13
} from "lexicons/api";
14
import { blobRefToSrc } from "src/utils/blobRefToSrc";
0
15
import { TextBlockCore, TextBlockCoreProps } from "./Blocks/TextBlockCore";
16
import { StaticMathBlock } from "./Blocks/StaticMathBlock";
17
import { codeToHtml, bundledLanguagesInfo, bundledThemesInfo } from "shiki";
···
119
}
120
case PubLeafletBlocksText.isMain(b.block):
121
return (
122
-
<p>
123
<StaticBaseTextBlock
124
facets={b.block.facets}
125
plaintext={b.block.plaintext}
···
130
case PubLeafletBlocksHeader.isMain(b.block): {
131
if (b.block.level === 1)
132
return (
133
-
<h1>
134
<StaticBaseTextBlock {...b.block} index={[]} />
135
</h1>
136
);
137
if (b.block.level === 2)
138
return (
139
-
<h2>
140
<StaticBaseTextBlock {...b.block} index={[]} />
141
</h2>
142
);
143
if (b.block.level === 3)
144
return (
145
-
<h3>
146
<StaticBaseTextBlock {...b.block} index={[]} />
147
</h3>
148
);
149
// if (b.block.level === 4) return <h4>{b.block.plaintext}</h4>;
150
// if (b.block.level === 5) return <h5>{b.block.plaintext}</h5>;
151
return (
152
-
<h6>
153
<StaticBaseTextBlock {...b.block} index={[]} />
154
</h6>
155
);
···
12
PubLeafletPagesLinearDocument,
13
} from "lexicons/api";
14
import { blobRefToSrc } from "src/utils/blobRefToSrc";
15
+
import { blockTextSize } from "src/utils/blockTextSize";
16
import { TextBlockCore, TextBlockCoreProps } from "./Blocks/TextBlockCore";
17
import { StaticMathBlock } from "./Blocks/StaticMathBlock";
18
import { codeToHtml, bundledLanguagesInfo, bundledThemesInfo } from "shiki";
···
120
}
121
case PubLeafletBlocksText.isMain(b.block):
122
return (
123
+
<p style={{ fontSize: blockTextSize.p }}>
124
<StaticBaseTextBlock
125
facets={b.block.facets}
126
plaintext={b.block.plaintext}
···
131
case PubLeafletBlocksHeader.isMain(b.block): {
132
if (b.block.level === 1)
133
return (
134
+
<h1 style={{ fontSize: blockTextSize.h1 }}>
135
<StaticBaseTextBlock {...b.block} index={[]} />
136
</h1>
137
);
138
if (b.block.level === 2)
139
return (
140
+
<h2 style={{ fontSize: blockTextSize.h2 }}>
141
<StaticBaseTextBlock {...b.block} index={[]} />
142
</h2>
143
);
144
if (b.block.level === 3)
145
return (
146
+
<h3 style={{ fontSize: blockTextSize.h3 }}>
147
<StaticBaseTextBlock {...b.block} index={[]} />
148
</h3>
149
);
150
// if (b.block.level === 4) return <h4>{b.block.plaintext}</h4>;
151
// if (b.block.level === 5) return <h5>{b.block.plaintext}</h5>;
152
return (
153
+
<h6 style={{ fontSize: blockTextSize.h4 }}>
154
<StaticBaseTextBlock {...b.block} index={[]} />
155
</h6>
156
);
+20
-5
components/Blocks/TextBlock/index.tsx
···
25
import { DotLoader } from "components/utils/DotLoader";
26
import { useMountProsemirror } from "./mountProsemirror";
27
import { schema } from "./schema";
0
28
29
import { Mention, MentionAutocomplete } from "components/Mention";
30
import { addMentionToEditor } from "app/[leaflet_id]/publish/BskyPostEditorProsemirror";
31
32
const HeadingStyle = {
33
-
1: "text-xl font-bold [font-family:var(--theme-heading-font)]",
34
-
2: "text-lg font-bold [font-family:var(--theme-heading-font)]",
35
-
3: "text-base font-bold text-secondary [font-family:var(--theme-heading-font)]",
0
0
0
0
0
0
36
} as { [level: number]: string };
37
38
export function TextBlock(
···
162
}
163
return (
164
<div
165
-
style={{ wordBreak: "break-word" }} // better than tailwind break-all!
0
0
0
166
className={`
167
${alignmentClass}
168
${props.type === "blockquote" ? (props.previousBlock?.type === "blockquote" ? `blockquote pt-3 ` : "blockquote") : ""}
···
266
// unless we break *only* on urls, this is better than tailwind 'break-all'
267
// b/c break-all can cause breaks in the middle of words, but break-word still
268
// forces break if a single text string (e.g. a url) spans more than a full line
269
-
style={{ wordBreak: "break-word", fontFamily: props.type === "heading" ? "var(--theme-heading-font)" : "var(--theme-font)" }}
0
0
0
0
270
className={`
271
${alignmentClass}
272
grow resize-none align-top whitespace-pre-wrap bg-transparent
···
290
props.nextBlock === null ? (
291
// if this is the only block on the page and is empty or is a canvas, show placeholder
292
<div
0
293
className={`${props.className} ${alignmentClass} w-full pointer-events-none absolute top-0 left-0 italic text-tertiary flex flex-col
294
${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle}
295
`}
···
25
import { DotLoader } from "components/utils/DotLoader";
26
import { useMountProsemirror } from "./mountProsemirror";
27
import { schema } from "./schema";
28
+
import { blockTextSize } from "src/utils/blockTextSize";
29
30
import { Mention, MentionAutocomplete } from "components/Mention";
31
import { addMentionToEditor } from "app/[leaflet_id]/publish/BskyPostEditorProsemirror";
32
33
const HeadingStyle = {
34
+
1: "font-bold [font-family:var(--theme-heading-font)]",
35
+
2: "font-bold [font-family:var(--theme-heading-font)]",
36
+
3: "font-bold text-secondary [font-family:var(--theme-heading-font)]",
37
+
} as { [level: number]: string };
38
+
39
+
const headingFontSize = {
40
+
1: blockTextSize.h1,
41
+
2: blockTextSize.h2,
42
+
3: blockTextSize.h3,
43
} as { [level: number]: string };
44
45
export function TextBlock(
···
169
}
170
return (
171
<div
172
+
style={{
173
+
wordBreak: "break-word",
174
+
...(props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : {}),
175
+
}}
176
className={`
177
${alignmentClass}
178
${props.type === "blockquote" ? (props.previousBlock?.type === "blockquote" ? `blockquote pt-3 ` : "blockquote") : ""}
···
276
// unless we break *only* on urls, this is better than tailwind 'break-all'
277
// b/c break-all can cause breaks in the middle of words, but break-word still
278
// forces break if a single text string (e.g. a url) spans more than a full line
279
+
style={{
280
+
wordBreak: "break-word",
281
+
fontFamily: props.type === "heading" ? "var(--theme-heading-font)" : "var(--theme-font)",
282
+
...(props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : {}),
283
+
}}
284
className={`
285
${alignmentClass}
286
grow resize-none align-top whitespace-pre-wrap bg-transparent
···
304
props.nextBlock === null ? (
305
// if this is the only block on the page and is empty or is a canvas, show placeholder
306
<div
307
+
style={props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : undefined}
308
className={`${props.className} ${alignmentClass} w-full pointer-events-none absolute top-0 left-0 italic text-tertiary flex flex-col
309
${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle}
310
`}
+12
-83
src/fonts.ts
···
6
displayName: string;
7
fontFamily: string;
8
fallback: string[];
0
9
} & (
10
| {
11
// Self-hosted fonts with local files
···
33
id: "quattro",
34
displayName: "iA Writer Quattro",
35
fontFamily: "iA Writer Quattro V",
0
36
type: "local",
37
files: [
38
{
···
52
id: "lora",
53
displayName: "Lora",
54
fontFamily: "Lora",
0
55
type: "local",
56
files: [
57
{
···
67
],
68
fallback: ["Georgia", "serif"],
69
},
70
-
"source-sans": {
71
-
id: "source-sans",
72
-
displayName: "Source Sans",
73
-
fontFamily: "Source Sans 3",
74
-
type: "local",
75
-
files: [
76
-
{
77
-
path: "/fonts/SourceSans3-Variable.woff2",
78
-
style: "normal",
79
-
weight: "200 900",
80
-
},
81
-
{
82
-
path: "/fonts/SourceSans3-Italic-Variable.woff2",
83
-
style: "italic",
84
-
weight: "200 900",
85
-
},
86
-
],
87
-
fallback: ["system-ui", "sans-serif"],
88
-
},
89
"atkinson-hyperlegible": {
90
id: "atkinson-hyperlegible",
91
displayName: "Atkinson Hyperlegible",
92
fontFamily: "Atkinson Hyperlegible Next",
0
93
type: "google",
94
googleFontsFamily:
95
"Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800",
96
fallback: ["system-ui", "sans-serif"],
97
},
98
-
"space-mono": {
99
-
id: "space-mono",
100
-
displayName: "Space Mono",
101
-
fontFamily: "Space Mono",
102
-
type: "google",
103
-
googleFontsFamily: "Space+Mono:ital,wght@0,400;0,700;1,400;1,700",
104
-
fallback: ["monospace"],
105
-
},
106
-
107
// Additional Google Fonts - Mono
108
"sometype-mono": {
109
id: "sometype-mono",
110
displayName: "Sometype Mono",
111
fontFamily: "Sometype Mono",
0
112
type: "google",
113
googleFontsFamily: "Sometype+Mono:ital,wght@0,400;0,700;1,400;1,700",
114
fallback: ["monospace"],
115
},
116
117
// Additional Google Fonts - Sans
118
-
"pt-sans": {
119
-
id: "pt-sans",
120
-
displayName: "PT Sans",
121
-
fontFamily: "PT Sans",
122
-
type: "google",
123
-
googleFontsFamily: "PT+Sans:ital,wght@0,400;0,700;1,400;1,700",
124
-
fallback: ["system-ui", "sans-serif"],
125
-
},
126
montserrat: {
127
id: "montserrat",
128
displayName: "Montserrat",
129
fontFamily: "Montserrat",
0
130
type: "google",
131
googleFontsFamily: "Montserrat:ital,wght@0,400;0,700;1,400;1,700",
132
fallback: ["system-ui", "sans-serif"],
133
},
134
-
"alegreya-sans": {
135
-
id: "alegreya-sans",
136
-
displayName: "Alegreya Sans",
137
-
fontFamily: "Alegreya Sans",
0
138
type: "google",
139
-
googleFontsFamily: "Alegreya+Sans:ital,wght@0,400;0,700;1,400;1,700",
140
fallback: ["system-ui", "sans-serif"],
141
-
},
142
-
143
-
// Additional Google Fonts - Serif
144
-
"pt-serif": {
145
-
id: "pt-serif",
146
-
displayName: "PT Serif",
147
-
fontFamily: "PT Serif",
148
-
type: "google",
149
-
googleFontsFamily: "PT+Serif:ital,wght@0,400;0,700;1,400;1,700",
150
-
fallback: ["Georgia", "serif"],
151
-
},
152
-
"crimson-text": {
153
-
id: "crimson-text",
154
-
displayName: "Crimson Text",
155
-
fontFamily: "Crimson Text",
156
-
type: "google",
157
-
googleFontsFamily: "Crimson+Text:ital,wght@0,400;0,700;1,400;1,700",
158
-
fallback: ["Georgia", "serif"],
159
-
},
160
-
cardo: {
161
-
id: "cardo",
162
-
displayName: "Cardo",
163
-
fontFamily: "Cardo",
164
-
type: "google",
165
-
googleFontsFamily: "Cardo:ital,wght@0,400;0,700;1,400",
166
-
fallback: ["Georgia", "serif"],
167
-
},
168
-
"ibm-plex-serif": {
169
-
id: "ibm-plex-serif",
170
-
displayName: "IBM Plex Serif",
171
-
fontFamily: "IBM Plex Serif",
172
-
type: "google",
173
-
googleFontsFamily: "IBM+Plex+Serif:ital,wght@0,400;0,700;1,400;1,700",
174
-
fallback: ["Georgia", "serif"],
175
-
},
176
-
"source-serif": {
177
-
id: "source-serif",
178
-
displayName: "Source Serif 4",
179
-
fontFamily: "Source Serif 4",
180
-
type: "google",
181
-
googleFontsFamily: "Source+Serif+4:ital,wght@0,400;0,700;1,400;1,700",
182
-
fallback: ["Georgia", "serif"],
183
},
184
};
185
···
6
displayName: string;
7
fontFamily: string;
8
fallback: string[];
9
+
baseSize?: number; // base font size in px for document content
10
} & (
11
| {
12
// Self-hosted fonts with local files
···
34
id: "quattro",
35
displayName: "iA Writer Quattro",
36
fontFamily: "iA Writer Quattro V",
37
+
baseSize: 16,
38
type: "local",
39
files: [
40
{
···
54
id: "lora",
55
displayName: "Lora",
56
fontFamily: "Lora",
57
+
baseSize: 17,
58
type: "local",
59
files: [
60
{
···
70
],
71
fallback: ["Georgia", "serif"],
72
},
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
73
"atkinson-hyperlegible": {
74
id: "atkinson-hyperlegible",
75
displayName: "Atkinson Hyperlegible",
76
fontFamily: "Atkinson Hyperlegible Next",
77
+
baseSize: 18,
78
type: "google",
79
googleFontsFamily:
80
"Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800",
81
fallback: ["system-ui", "sans-serif"],
82
},
0
0
0
0
0
0
0
0
0
83
// Additional Google Fonts - Mono
84
"sometype-mono": {
85
id: "sometype-mono",
86
displayName: "Sometype Mono",
87
fontFamily: "Sometype Mono",
88
+
baseSize: 17,
89
type: "google",
90
googleFontsFamily: "Sometype+Mono:ital,wght@0,400;0,700;1,400;1,700",
91
fallback: ["monospace"],
92
},
93
94
// Additional Google Fonts - Sans
0
0
0
0
0
0
0
0
95
montserrat: {
96
id: "montserrat",
97
displayName: "Montserrat",
98
fontFamily: "Montserrat",
99
+
baseSize: 17,
100
type: "google",
101
googleFontsFamily: "Montserrat:ital,wght@0,400;0,700;1,400;1,700",
102
fallback: ["system-ui", "sans-serif"],
103
},
104
+
"source-sans": {
105
+
id: "source-sans",
106
+
displayName: "Source Sans 3",
107
+
fontFamily: "Source Sans 3",
108
+
baseSize: 18,
109
type: "google",
110
+
googleFontsFamily: "Source+Sans+3:ital,wght@0,400;0,700;1,400;1,700",
111
fallback: ["system-ui", "sans-serif"],
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
112
},
113
};
114
+7
src/utils/blockTextSize.ts
···
0
0
0
0
0
0
0
···
1
+
export const blockTextSize = {
2
+
p: "1em",
3
+
h1: "2em",
4
+
h2: "1.5em",
5
+
h3: "1.25em",
6
+
h4: "1.125em",
7
+
} as const;