a tool for shared writing and social publishing

fix font sizes

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