{"id":2686,"date":"2025-09-05T09:38:50","date_gmt":"2025-09-05T12:38:50","guid":{"rendered":"https:\/\/benjaminray.com\/codebase\/?p=2686"},"modified":"2025-09-05T09:42:54","modified_gmt":"2025-09-05T12:42:54","slug":"smooth-font-size-scaling-between-two-viewport-widths","status":"publish","type":"post","link":"https:\/\/benjaminray.com\/codebase\/smooth-font-size-scaling-between-two-viewport-widths\/","title":{"rendered":"Smooth Font Size Scaling Between Two Viewport Widths"},"content":{"rendered":"<p>It's common to want fluid scaling of a font size on medium screens where a page is facing reduced width but not enough to switch to the mobile layout. For example, if a page's mobile layout kicks in at 799px, items might be squeezed a bit between 800px and 999px.<\/p>\n<p>The CSS below sets the font size to 22px on mobile and desktop (\u2264799px and \u22651000px), but smoothly decreases from 22px to 18px when the viewport is between 800px and 999px.<\/p>\n<blockquote><p>\n  Caveat: We need to maintain the number used in the division within the <code>calc()<\/code>. It's max width (999) minus min width (800), or 199 in this case.\n<\/p><\/blockquote>\n<h2>Example with <code>px<\/code> Sizing<\/h2>\n<pre><code class=\"language-css\">h2.some-class {\n  font-size: 22px; \/* 22px at \u2264799px and \u22651000px *\/\n}\n\n\/* Fluid only between 800px and 999px *\/\n@media (min-width: 800px) and (max-width: 999px) {\n  h2.some-class {\n    \/* 22px at 999px \u2192 18px at 800px (smoothly decreasing) *\/\n    font-size: clamp(\n      18px \/* min size *\/,\n      calc(18px + (4 * ((100vw - 800px) \/ 199))) \/* fluid size *\/,\n      22px \/* max size *\/\n    );\n  }\n}\n<\/code><\/pre>\n<h2>Example with <code>em<\/code> Sizing (sort of)<\/h2>\n<p>We can't use relative units like <code>em<\/code> for the dynamic scaling because <code>em<\/code> inside <code>calc()<\/code> is relative to the current element's font size. Since we're trying to calculate the font size itself, the math collapses and it just sticks to the upper bound (1.375em).<\/p>\n<p>While the calculations must use absolute units (<code>px<\/code>), we can still use relative units (<code>em<\/code>) everywhere else.<\/p>\n<p>If we care that the font size transitions smoothly from the dynamic size (calculated) to the regular size (in <code>em<\/code>) as the width straddles the top of the dynamic range (999px <--> 1000px), we need to calculate the <code>px<\/code> equivalent of the dynamic size and use that in the <code>clamp()<\/code> calculation. In this case, the relative size is 1.375em which we know is 22px (assuming our relative root size is 16px).<\/p>\n<pre><code class=\"language-css\">h2.some-class {\n  font-size: 1.375em; \/* 1.375em (22px) at \u2264799px and \u22651000px *\/\n}\n\n@media (min-width: 800px) and (max-width: 999px) {\n  h2.some-class {\n    \/* 22px at 999px \u2192 18px at 800px (smoothly decreasing) *\/\n    font-size: clamp(\n      18px \/* min size *\/,\n      calc(18px + (4 * ((100vw - 800px) \/ 199))) \/* fluid size *\/,\n      22px \/* max size *\/\n    );\n  }\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s common to want fluid scaling of a font size on medium screens where a page is facing reduced width but not enough to switch to the mobile layout. For  [&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[16],"tags":[54,56,55],"class_list":["post-2686","post","type-post","status-publish","format-standard","hentry","category-css","tag-dynamic-width","tag-font-size","tag-media-query"],"acf":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9GNjN-Hk","jetpack_likes_enabled":false,"_links":{"self":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/2686","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/comments?post=2686"}],"version-history":[{"count":5,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/2686\/revisions"}],"predecessor-version":[{"id":2691,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/posts\/2686\/revisions\/2691"}],"wp:attachment":[{"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/media?parent=2686"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/categories?post=2686"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benjaminray.com\/codebase\/wp-json\/wp\/v2\/tags?post=2686"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}