a tool for shared writing and social publishing

add underlines to link if the text is similar to the accent color

+88 -20
+32 -10
app/globals.css
··· 96 96 --accent-2: 255, 255, 255; 97 97 --accent-contrast: 0, 0, 225; 98 98 --accent-1-is-contrast: "true"; 99 + --accent-contrast-similar-to-text: 0; 100 + --link-underline: none; 99 101 --accent-light: color-mix( 100 102 in oklab, 101 103 rgb(var(--accent-contrast)), ··· 189 191 a { 190 192 @apply text-accent-contrast; 191 193 @apply hover:cursor-pointer; 192 - @apply no-underline; 193 194 @apply hover:underline; 195 + text-decoration-line: var(--link-underline, none); 194 196 } 195 197 196 198 p { ··· 215 217 .pageScrollWrapper h2, 216 218 .pageScrollWrapper h3, 217 219 .pageScrollWrapper h4 { 218 - font-family: var(--theme-heading-font, var(--theme-font, var(--font-quattro))); 220 + font-family: var( 221 + --theme-heading-font, 222 + var(--theme-font, var(--font-quattro)) 223 + ); 219 224 } 220 225 221 226 /* Scale heading sizes relative to the custom base font size */ 222 - .pageScrollWrapper h1 { font-size: 2em; } 223 - .pageScrollWrapper h2 { font-size: 1.625em; } 224 - .pageScrollWrapper h3 { font-size: 1.125em; } 225 - .pageScrollWrapper h4 { font-size: 1em; } 227 + .pageScrollWrapper h1 { 228 + font-size: 2em; 229 + } 230 + .pageScrollWrapper h2 { 231 + font-size: 1.625em; 232 + } 233 + .pageScrollWrapper h3 { 234 + font-size: 1.125em; 235 + } 236 + .pageScrollWrapper h4 { 237 + font-size: 1em; 238 + } 226 239 227 240 /* Scale text size classes relative to the custom base font size. 228 241 Tailwind's text-sm/text-base/text-lg compile to fixed rem values 229 242 which ignore the custom base size set on .pageScrollWrapper. */ 230 - .pageScrollWrapper .textSizeSmall { font-size: 0.875em; } 231 - .pageScrollWrapper .textSizeLarge { font-size: 1.125em; } 243 + .pageScrollWrapper .textSizeSmall { 244 + font-size: 0.875em; 245 + } 246 + .pageScrollWrapper .textSizeLarge { 247 + font-size: 1.125em; 248 + } 232 249 233 250 .pageScrollWrapper pre { 234 251 font-family: var(--theme-font, var(--font-quattro)); ··· 243 260 .pubWrapper h2, 244 261 .pubWrapper h3, 245 262 .pubWrapper h4 { 246 - font-family: var(--theme-heading-font, var(--theme-font, var(--font-quattro))); 263 + font-family: var( 264 + --theme-heading-font, 265 + var(--theme-font, var(--font-quattro)) 266 + ); 247 267 } 248 268 /*END FONT STYLING*/ 249 269 ··· 571 591 .footnote-side-item { 572 592 max-height: 4.5em; 573 593 overflow: hidden; 574 - transition: max-height 200ms ease, mask-image 200ms ease; 594 + transition: 595 + max-height 200ms ease, 596 + mask-image 200ms ease; 575 597 } 576 598 .footnote-side-item.has-overflow { 577 599 mask-image: linear-gradient(to bottom, white 50%, transparent 100%);
+56 -10
components/ThemeManager/ThemeProvider.tsx
··· 29 29 PublicationThemeProvider, 30 30 } from "./PublicationThemeProvider"; 31 31 import { getColorDifference } from "./themeUtils"; 32 - import { getFontConfig, getGoogleFontsUrl, getFontFamilyValue, getFontBaseSize, defaultFontId } from "src/fonts"; 32 + import { 33 + getFontConfig, 34 + getGoogleFontsUrl, 35 + getFontFamilyValue, 36 + getFontBaseSize, 37 + defaultFontId, 38 + } from "src/fonts"; 33 39 34 40 // define a function to set an Aria Color to a CSS Variable in RGB 35 41 function setCSSVariableToColor( ··· 89 95 90 96 let pageWidth = useEntity(props.entityID, "theme/page-width"); 91 97 // Use initial font IDs as fallback until Replicache syncs 92 - let headingFontId = useEntity(props.entityID, "theme/heading-font")?.data.value ?? props.initialHeadingFontId; 93 - let bodyFontId = useEntity(props.entityID, "theme/body-font")?.data.value ?? props.initialBodyFontId; 98 + let headingFontId = 99 + useEntity(props.entityID, "theme/heading-font")?.data.value ?? 100 + props.initialHeadingFontId; 101 + let bodyFontId = 102 + useEntity(props.entityID, "theme/body-font")?.data.value ?? 103 + props.initialBodyFontId; 94 104 95 105 return ( 96 106 <CardBorderHiddenContext.Provider value={!!cardBorderHiddenValue}> ··· 192 202 accentContrast = sortedAccents[0]; 193 203 } 194 204 205 + // Check if the final accent contrast color is very similar to the text color 206 + let accentContrastSimilarToText = 207 + getColorDifference( 208 + colorToString(accentContrast, "rgb"), 209 + colorToString(primary, "rgb"), 210 + ) < 0.2; 211 + 195 212 // Get font configs for CSS variables. 196 213 // When using the default font (Quattro), use var(--font-quattro) which is 197 214 // always available via next/font/local in layout.tsx, rather than the raw ··· 203 220 const headingFontConfig = getFontConfig(headingFontId); 204 221 const bodyFontConfig = getFontConfig(bodyFontId); 205 222 const headingFontValue = isDefaultHeading 206 - ? (isDefaultBody ? undefined : "var(--font-quattro)") 223 + ? isDefaultBody 224 + ? undefined 225 + : "var(--font-quattro)" 207 226 : getFontFamilyValue(headingFontConfig); 208 227 const bodyFontValue = isDefaultBody 209 - ? (isDefaultHeading ? undefined : "var(--font-quattro)") 228 + ? isDefaultHeading 229 + ? undefined 230 + : "var(--font-quattro)" 210 231 : getFontFamilyValue(bodyFontConfig); 211 - const bodyFontBaseSize = isDefaultBody ? undefined : getFontBaseSize(bodyFontConfig); 232 + const bodyFontBaseSize = isDefaultBody 233 + ? undefined 234 + : getFontBaseSize(bodyFontConfig); 212 235 const headingGoogleFontsUrl = getGoogleFontsUrl(headingFontConfig); 213 236 const bodyGoogleFontsUrl = getGoogleFontsUrl(bodyFontConfig); 214 237 ··· 222 245 if (existingLink) return; 223 246 224 247 // Add preconnect hints if not present 225 - if (!document.querySelector('link[href="https://fonts.googleapis.com"]')) { 248 + if ( 249 + !document.querySelector('link[href="https://fonts.googleapis.com"]') 250 + ) { 226 251 const preconnect1 = document.createElement("link"); 227 252 preconnect1.rel = "preconnect"; 228 253 preconnect1.href = "https://fonts.googleapis.com"; ··· 249 274 250 275 loadGoogleFont(headingGoogleFontsUrl, headingFontConfig.fontFamily); 251 276 loadGoogleFont(bodyGoogleFontsUrl, bodyFontConfig.fontFamily); 252 - }, [headingGoogleFontsUrl, bodyGoogleFontsUrl, headingFontConfig.fontFamily, bodyFontConfig.fontFamily]); 277 + }, [ 278 + headingGoogleFontsUrl, 279 + bodyGoogleFontsUrl, 280 + headingFontConfig.fontFamily, 281 + bodyFontConfig.fontFamily, 282 + ]); 253 283 254 284 useEffect(() => { 255 285 if (local) return; ··· 293 323 "--accent-1-is-contrast", 294 324 accentContrast === accent1 ? "1" : "0", 295 325 ); 326 + el?.style.setProperty( 327 + "--accent-contrast-similar-to-text", 328 + accentContrastSimilarToText ? "1" : "0", 329 + ); 330 + el?.style.setProperty( 331 + "--link-underline", 332 + accentContrastSimilarToText ? "underline" : "none", 333 + ); 296 334 297 335 // Set page width CSS variable 298 336 el?.style.setProperty( 299 337 "--page-width-setting", 300 338 (pageWidth || 624).toString(), 301 339 ); 302 - 303 340 }, [ 304 341 local, 305 342 bgLeaflet, ··· 311 348 accent1, 312 349 accent2, 313 350 accentContrast, 351 + accentContrastSimilarToText, 314 352 pageWidth, 315 353 ]); 316 354 return ( ··· 326 364 "--accent-2": colorToString(accent2, "rgb"), 327 365 "--accent-contrast": colorToString(accentContrast, "rgb"), 328 366 "--accent-1-is-contrast": accentContrast === accent1 ? 1 : 0, 367 + "--accent-contrast-similar-to-text": accentContrastSimilarToText 368 + ? 1 369 + : 0, 370 + "--link-underline": accentContrastSimilarToText 371 + ? "underline" 372 + : "none", 329 373 "--highlight-1": highlight1 330 374 ? `rgb(${colorToString(parseColor(`hsba(${highlight1})`), "rgb")})` 331 375 : "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)", ··· 336 380 "--page-width-units": `min(${pageWidth || 624}px, calc(100vw - 12px))`, 337 381 "--theme-heading-font": headingFontValue, 338 382 "--theme-font": bodyFontValue, 339 - "--theme-font-base-size": bodyFontBaseSize ? `${bodyFontBaseSize}px` : undefined, 383 + "--theme-font-base-size": bodyFontBaseSize 384 + ? `${bodyFontBaseSize}px` 385 + : undefined, 340 386 } as CSSProperties 341 387 } 342 388 >