a tool for shared writing and social publishing

fix font sizes

+47 -16
+13
app/globals.css
··· 205 205 /* Scope custom fonts to document content only (not sidebar/UI chrome) */ 206 206 .pageScrollWrapper { 207 207 font-family: var(--theme-font, var(--font-quattro)); 208 + font-size: var(--theme-font-base-size, 16px); 208 209 } 209 210 210 211 .pageScrollWrapper h1, ··· 213 214 .pageScrollWrapper h4 { 214 215 font-family: var(--theme-heading-font, var(--theme-font, var(--font-quattro))); 215 216 } 217 + 218 + /* Scale heading sizes relative to the custom base font size */ 219 + .pageScrollWrapper h1 { font-size: 2em; } 220 + .pageScrollWrapper h2 { font-size: 1.625em; } 221 + .pageScrollWrapper h3 { font-size: 1.125em; } 222 + .pageScrollWrapper h4 { font-size: 1em; } 223 + 224 + /* Scale text size classes relative to the custom base font size. 225 + Tailwind's text-sm/text-base/text-lg compile to fixed rem values 226 + which ignore the custom base size set on .pageScrollWrapper. */ 227 + .pageScrollWrapper .textSizeSmall { font-size: 0.875em; } 228 + .pageScrollWrapper .textSizeLarge { font-size: 1.125em; } 216 229 217 230 .pageScrollWrapper pre { 218 231 font-family: var(--theme-font, var(--font-quattro));
+1 -1
app/lish/[did]/[publication]/[rkey]/Blocks/PublishedPageBlock.tsx
··· 111 111 <div className="grow"> 112 112 {title && ( 113 113 <div 114 - className={`pageBlockOne outline-none resize-none align-top gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`} 114 + className={`pageBlockOne outline-none resize-none align-top gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`} 115 115 > 116 116 <TextBlock 117 117 facets={title.facets}
+2
app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer.tsx
··· 18 18 import { getDocumentPages, hasLeafletContent } from "src/utils/normalizeRecords"; 19 19 import { DocumentProvider } from "contexts/DocumentContext"; 20 20 import { LeafletContentProvider } from "contexts/LeafletContentContext"; 21 + import { FontLoader } from "components/FontLoader"; 21 22 22 23 export async function DocumentPageRenderer({ 23 24 did, ··· 118 119 return ( 119 120 <DocumentProvider value={document}> 120 121 <LeafletContentProvider value={{ pages }}> 122 + <FontLoader headingFontId={document.theme?.headingFont} bodyFontId={document.theme?.bodyFont} /> 121 123 <PublicationThemeProvider theme={document.theme} pub_creator={pub_creator} isStandalone={isStandalone}> 122 124 <PublicationBackgroundProvider theme={document.theme} pub_creator={pub_creator}> 123 125 <LeafletLayout>
+7 -7
app/lish/[did]/[publication]/[rkey]/PostContent.tsx
··· 263 263 <div className="pt-2 pb-2 px-3 grow min-w-0"> 264 264 <div className="flex flex-col w-full min-w-0 h-full grow "> 265 265 <div 266 - className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 266 + className={`linkBlockTitle bg-transparent -mb-0.5 border-none font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 267 267 style={{ 268 268 overflow: "hidden", 269 269 textOverflow: "ellipsis", ··· 364 364 case PubLeafletBlocksHeader.isMain(b.block): { 365 365 if (b.block.level === 1) 366 366 return ( 367 - <h2 style={{ fontSize: blockTextSize.h1 }} className={`h1Block ${className}`} {...blockProps}> 367 + <h1 style={{ fontSize: blockTextSize.h1 }} className={`h1Block ${className}`} {...blockProps}> 368 368 <TextBlock 369 369 {...b.block} 370 370 index={index} 371 371 preview={preview} 372 372 pageId={pageId} 373 373 /> 374 - </h2> 374 + </h1> 375 375 ); 376 376 if (b.block.level === 2) 377 377 return ( 378 - <h3 style={{ fontSize: blockTextSize.h2 }} className={`h2Block ${className}`} {...blockProps}> 378 + <h2 style={{ fontSize: blockTextSize.h2 }} className={`h2Block ${className}`} {...blockProps}> 379 379 <TextBlock 380 380 {...b.block} 381 381 index={index} 382 382 preview={preview} 383 383 pageId={pageId} 384 384 /> 385 - </h3> 385 + </h2> 386 386 ); 387 387 if (b.block.level === 3) 388 388 return ( 389 - <h4 style={{ fontSize: blockTextSize.h3 }} className={`h3Block ${className}`} {...blockProps}> 389 + <h3 style={{ fontSize: blockTextSize.h3 }} className={`h3Block ${className}`} {...blockProps}> 390 390 <TextBlock 391 391 {...b.block} 392 392 index={index} 393 393 preview={preview} 394 394 pageId={pageId} 395 395 /> 396 - </h4> 396 + </h3> 397 397 ); 398 398 // if (b.block.level === 4) return <h4>{b.block.plaintext}</h4>; 399 399 // if (b.block.level === 5) return <h5>{b.block.plaintext}</h5>;
+1 -1
components/Blocks/ExternalLinkBlock.tsx
··· 78 78 <div className="pt-2 pb-2 px-3 grow min-w-0"> 79 79 <div className="flex flex-col w-full min-w-0 h-full grow "> 80 80 <div 81 - className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 81 + className={`linkBlockTitle bg-transparent -mb-0.5 border-none font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 82 82 style={{ 83 83 overflow: "hidden", 84 84 textOverflow: "ellipsis",
+1 -1
components/Blocks/PageLinkBlock.tsx
··· 88 88 <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip "> 89 89 {leafletMetadata[0] && ( 90 90 <div 91 - className={`pageBlockOne outline-hidden resize-none align-top flex gap-2 ${leafletMetadata[0].type === "heading" ? "font-bold text-base" : ""}`} 91 + className={`pageBlockOne outline-hidden resize-none align-top flex gap-2 ${leafletMetadata[0].type === "heading" ? "font-bold" : ""}`} 92 92 > 93 93 {leafletMetadata[0].listData && ( 94 94 <ListMarker
+5 -5
components/Blocks/TextBlock/index.tsx
··· 138 138 }[alignment]; 139 139 let textStyle = 140 140 textSize?.data.value === "small" 141 - ? "text-sm" 141 + ? "textSizeSmall" 142 142 : textSize?.data.value === "large" 143 - ? "text-lg" 143 + ? "textSizeLarge" 144 144 : ""; 145 145 let { permissions } = useEntitySetContext(); 146 146 ··· 204 204 }[alignment]; 205 205 let textStyle = 206 206 textSize?.data.value === "small" 207 - ? "text-sm text-secondary" 207 + ? "textSizeSmall text-secondary" 208 208 : textSize?.data.value === "large" 209 - ? "text-lg text-primary" 210 - : "text-base text-primary"; 209 + ? "textSizeLarge text-primary" 210 + : "text-primary"; 211 211 212 212 let editorState = useEditorStates( 213 213 (s) => s.editorStates[props.entityID],
+3
components/FontLoader.tsx
··· 10 10 getFontPreloadLinks, 11 11 getGoogleFontsUrl, 12 12 getFontFamilyValue, 13 + getFontBaseSize, 13 14 } from "src/fonts"; 14 15 15 16 type FontLoaderProps = { ··· 53 54 54 55 const headingFontValue = getFontFamilyValue(headingFont); 55 56 const bodyFontValue = getFontFamilyValue(bodyFont); 57 + const bodyFontBaseSize = getFontBaseSize(bodyFont); 56 58 57 59 // Set font CSS variables scoped to .leafletWrapper so they don't affect app UI 58 60 const fontVariableCSS = ` 59 61 .leafletWrapper { 60 62 --theme-heading-font: ${headingFontValue}; 61 63 --theme-font: ${bodyFontValue}; 64 + --theme-font-base-size: ${bodyFontBaseSize}px; 62 65 } 63 66 `.trim(); 64 67
+5
components/ThemeManager/PublicationThemeProvider.tsx
··· 141 141 let highlight2 = useColorAttribute(null, "theme/highlight-2"); 142 142 let highlight3 = useColorAttribute(null, "theme/highlight-3"); 143 143 144 + let headingFontId = theme?.headingFont; 145 + let bodyFontId = theme?.bodyFont; 146 + 144 147 return { 145 148 bgLeaflet, 146 149 bgPage, ··· 152 155 highlight3, 153 156 showPageBackground, 154 157 pageWidth, 158 + headingFontId, 159 + bodyFontId, 155 160 }; 156 161 }; 157 162
+3 -1
components/ThemeManager/ThemeProvider.tsx
··· 22 22 PublicationThemeProvider, 23 23 } from "./PublicationThemeProvider"; 24 24 import { getColorDifference } from "./themeUtils"; 25 - import { getFontConfig, getGoogleFontsUrl, getFontFamilyValue, generateFontFaceCSS } from "src/fonts"; 25 + import { getFontConfig, getGoogleFontsUrl, getFontFamilyValue, generateFontFaceCSS, getFontBaseSize } from "src/fonts"; 26 26 27 27 // define a function to set an Aria Color to a CSS Variable in RGB 28 28 function setCSSVariableToColor( ··· 186 186 const bodyFontConfig = getFontConfig(bodyFontId); 187 187 const headingFontValue = getFontFamilyValue(headingFontConfig); 188 188 const bodyFontValue = getFontFamilyValue(bodyFontConfig); 189 + const bodyFontBaseSize = getFontBaseSize(bodyFontConfig); 189 190 const headingGoogleFontsUrl = getGoogleFontsUrl(headingFontConfig); 190 191 const bodyGoogleFontsUrl = getGoogleFontsUrl(bodyFontConfig); 191 192 ··· 313 314 "--page-width-units": `min(${pageWidth || 624}px, calc(100vw - 12px))`, 314 315 "--theme-heading-font": headingFontValue, 315 316 "--theme-font": bodyFontValue, 317 + "--theme-font-base-size": `${bodyFontBaseSize}px`, 316 318 } as CSSProperties 317 319 } 318 320 >
+6
src/fonts.ts
··· 113 113 }; 114 114 115 115 export const defaultFontId = "quattro"; 116 + export const defaultBaseSize = 16; 116 117 117 118 // Parse a Google Fonts URL or string to extract the font name and family parameter 118 119 // Supports various formats: ··· 235 236 export function getGoogleFontsUrl(font: FontConfig): string | null { 236 237 if (font.type !== "google") return null; 237 238 return `https://fonts.googleapis.com/css2?family=${font.googleFontsFamily}&display=swap`; 239 + } 240 + 241 + // Get the base font size for a font config 242 + export function getFontBaseSize(font: FontConfig): number { 243 + return font.baseSize ?? defaultBaseSize; 238 244 } 239 245 240 246 // Get the CSS font-family value with fallbacks