{"id":4294,"date":"2024-10-31T17:33:58","date_gmt":"2024-10-31T22:33:58","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=4294"},"modified":"2024-11-01T10:25:05","modified_gmt":"2024-11-01T15:25:05","slug":"edge-to-edge-text","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/edge-to-edge-text\/","title":{"rendered":"Edge to Edge Text"},"content":{"rendered":"\n<p>I kid you not: Roman Komarov&#8217;s <a href=\"https:\/\/kizu.dev\/fit-to-width-text\/\">Fit-to-Width Text<\/a> is one of my favorite CSS tricks I&#8217;ve ever seen. It&#8217;s, uh, quite a treat (that&#8217;s all you&#8217;re going to get here on Halloween, sorry). It&#8217;s just <em>so strange.<\/em> The end result is that you can size a line of text such that it hits the left and right edge of a container perfectly. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"996\" height=\"738\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.00%E2%80%AFPM.png?resize=996%2C738&#038;ssl=1\" alt=\"\" class=\"wp-image-4296\" style=\"width:395px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.00%E2%80%AFPM.png?w=996&amp;ssl=1 996w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.00%E2%80%AFPM.png?resize=300%2C222&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.00%E2%80%AFPM.png?resize=768%2C569&amp;ssl=1 768w\" sizes=\"auto, (max-width: 996px) 100vw, 996px\" \/><\/figure>\n<\/div>\n\n\n<p>This is a very legitimate need that people have been solving for ages. If the container is a fixed size, you can solve it by setting ultra specific font sizes. But more likely these days, containers are of unknown widths, leaving us to JavaScript for figuring out how big of text we can fit in there. <a href=\"http:\/\/fittextjs.com\/\">FitText<\/a> was a seminal example. These days, we can also do it with <a href=\"https:\/\/frontendmasters.com\/blog\/container-queries-and-units\/#what-are-container-query-units\">container units<\/a>, but it&#8217;s still extremely fiddly. Wouldn&#8217;t it be nice to be like <code>font-size: make-it-fit;<\/code>? <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"671\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?resize=1024%2C671&#038;ssl=1\" alt=\"\" class=\"wp-image-4297\" style=\"width:461px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?resize=1024%2C671&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?resize=300%2C197&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?resize=768%2C503&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?resize=1536%2C1007&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-12.41.41%E2%80%AFPM.png?w=1556&amp;ssl=1 1556w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<p>Roman&#8217;s trick is as close to that as can be. Check out <a href=\"https:\/\/frontendmasters.com\/blog\/container-queries-and-units\/#what-are-container-query-units\">his post<\/a> for all the details, but the core concept is that it uses scroll-driven animations. The text gets set <em>pretty big<\/em> by default, then a scroll-driven animation is set on it which runs scales the text <em>down <\/em>essentially until <code>animation-range: entry-crossing;<\/code> is fulfilled then stops. Here&#8217;s an example with just one word (free free to resize and see):<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ExqLrqg\" src=\"\/\/codepen.io\/anon\/embed\/ExqLrqg?height=450&amp;theme-id=47434&amp;slug-hash=ExqLrqg&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ExqLrqg\" title=\"CodePen Embed ExqLrqg\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The absolute core of the idea is:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@supports<\/span> (<span class=\"hljs-attribute\">animation-range:<\/span> entry-crossing) {\n  <span class=\"hljs-selector-class\">.fit-to-width<\/span> {\n    <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">12rem<\/span>; <span class=\"hljs-comment\">\/* max-font-size *\/<\/span>\n    <span class=\"hljs-attribute\">overflow<\/span>: hidden;\n\n    &amp; &gt; * {\n      <span class=\"hljs-attribute\">inline-size<\/span>: max-content;\n      <span class=\"hljs-attribute\">transform-origin<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">animation<\/span>: apply-text-ratio linear;\n      <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">view<\/span>(inline);\n      <span class=\"hljs-attribute\">animation-range<\/span>: entry-crossing;\n      <span class=\"hljs-attribute\">display<\/span>: block;\n    }\n  }\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> apply-text-ratio {\n  <span class=\"hljs-selector-tag\">from<\/span> {\n    <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">margin-block-end<\/span>: -<span class=\"hljs-number\">1<\/span>lh;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Like Roman&#8217;s original demo, it works great on multiple lines, actually showing off the power of the technique much better. The design of &#8220;multiple lines sized to fit exactly on a line&#8221; made me think of those &#8220;In this house we believe&#8230;&#8221; signs, so I made my own:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_bGXexKz\" src=\"\/\/codepen.io\/anon\/embed\/bGXexKz?height=650&amp;theme-id=47434&amp;slug-hash=bGXexKz&amp;default-tab=result\" height=\"650\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed bGXexKz\" title=\"CodePen Embed bGXexKz\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That demo has <code>contenteditable<\/code> on it so you can mess with the letters and see it work. <\/p>\n\n\n\n<p>If, like me, you have a hard time wrapping your mind around the trick, note that you can inspect the animations in Chrome DevTools and see how each span has a different length of animation:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"292\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=1024%2C292&#038;ssl=1\" alt=\"\" class=\"wp-image-4298\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=1024%2C292&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=300%2C86&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=768%2C219&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=1536%2C439&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.27.35%E2%80%AFPM.png?resize=2048%2C585&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>I think the longer the animation the more the text <em>scales down toward zero.<\/em> Phew \u2014&nbsp;I told you it was weird.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A rather exceptional CSS trick discovered by Roman Komarov that uses scroll driven animations to resize lines of text to fit exactly to their container.<\/p>\n","protected":false},"author":1,"featured_media":4299,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,57,39],"class_list":["post-4294","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-scroll-driven-animations","tag-typography"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-31-at-3.32.44%E2%80%AFPM.png?fit=1000%2C499&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4294","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=4294"}],"version-history":[{"count":3,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4294\/revisions"}],"predecessor-version":[{"id":4317,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4294\/revisions\/4317"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/4299"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=4294"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=4294"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=4294"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}