a tool for shared writing and social publishing

handle header sizes and base size for fonts

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