{
  "$type": "site.standard.document",
  "content": {
    "$type": "site.standard.content.markdown",
    "text": "I’m in the middle of deep thought about what a text component looks like. There’s the easy stuff like having the ability to choose the appropriate semantic element. There’s the less obvious stuff, like adding a flag for [screenreader only content](/posts/screenreader-only/). But then there’s something that we tend to overlook, how to manage overflow content.\n\n## Data loss\n\nIt’s not uncommon for someone to choose the solution of clipping the content, maybe in the form of an ellipsis. We even have more options coming about [when those ellipsis appear](https://developer.mozilla.org/en-US/docs/Web/CSS/line-clamp). The problem with approaches that include `overflow: hidden;` is that the content is no longer clearly accessible. This goes against a core principle of mine: if you place it on the page, that means it is important. There should be no condition that obstructs that element in any way. Otherwise that can introduce data loss. Adding ellipsis is not an inclusive solution to large blocks of text. Instead, we’ll need to ensure the text remains readable while balancing the expectations of the layout.\n\n## Properties\n\nThere’s several CSS properties that are directly related to the way text flows on a webpage. We’ll go over each of the properties below but first a few definitions:\n\n-   **Whitespace** refers to characters that provide space between other characters.\n-   **Segment breaks** are characters that cause text to break onto new lines (eg., line feed).\n-   **Hard breaks** (aka: forced line break) will always cause the text to break onto a new line, even if it is not necessary to do so.\n-   **Soft breaks** will cause text to break to a new line only if necessary. Where these can occur are called soft wrap opportunities.\n-   **Line box** describes the inline formatted box, typically used to contain text. The size of this box depends on the content and the available size of its ancestors.\n-   **CJK** is short for Chinese/Japanese/Korean used to describe text that has a different ruleset for when strings are allowed to break.\n\n### `white-space`\n\nThis property has been around for a while, but recently it has been altered to host separate properties. Overall, this property and its related new properties are meant to handle how whitespace characters effect the surrounding text.\n\nFirst, the `white-space-collapse` property has the following possible values:\n\n-   `collapse` says that all whitespace should be collapsed. How whitespace is collapsed [follows several algorithmic steps](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space-collapse#collapsing_of_white_space).\n-   `preserve` says whitespace and segment break characters are perserved. This is commonly seen as the default in the [`<pre/>` HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/pre) style.\n-   `preserve-breaks` says only preserve segment breaks, whitespace characters are collapsed.\n-   `preserve-spaces` does the opposite of `preserve-breaks` by allowing segment breaks to be collapsed and perserve whitespace as written in source.\n-   `break-spaces` is the most unique of the values with a more specific set of rules for how whitespace is perserved.\n\nRemember that `white-space-collapse` is only one part of the `white-space` property. The other part is `text-wrap-mode` which only has two values, `wrap` and `nowrap`. As you might expect, you’ll primarly want to have `text-wrap-mode: wrap` and this is the default.\n\nWhen considering the keyword options provided by the original `white-space` property, it’s not immediately clear how they align to these values:\n\n-   `normal` is `white-space-collapse: collapse; text-wrap-mode: wrap`.\n-   `pre` is `white-space-collapse: preserve; text-wrap-mode: nowrap`.\n-   `pre-line` is `white-space-collapse: preserve-breaks; text-wrap-mode: wrap`.\n-   `pre-wrap` is `white-space-collapse: preserve; text-wrap-mode: wrap`.\n\nThere is no keyword analog under `white-space` to get `white-space-collapse: preserve-spaces` behavior. At the time of this writing, this is only available in Firefox.\n\n### `text-wrap`\n\nTo make things more confusing, `text-wrap` is also a shorthand. We already saw one of the properties `text-wrap-mode` which determines if the text is allowed to wrap or not. The newer part to this is `text-wrap-style` which has added many of the fancy algorithmic ways that text can wrap based on the number of characters. Here are the values for this property:\n\n-   `auto` wraps the text in the most performant way and ignores the number of characters. This is the default.\n-   `balance` attempts to wrap in away that keeps the number of characters roughly equal amongst the lines of text. This is best used for headlines.\n-   `pretty` is similar to balance but favors layout over speed. This is best used for paragraphs of text.\n-   `stable` is meant to be applied in areas where the user is editing content and wrapping should be kept to a minimum during this time.\n\n[Harry Roberts](https://csswizardry.com/) [posted some benchmarks](https://x.com/csswizardry/status/1725543814587134435) on using the newer `text-wrap` values. The setup was a page with 10,000 `<p/>` elements which, by default, have orphans. Here’s what he found:\n\n-   Baseline – 1,186ms\n-   `text-wrap: balance`: 1,224ms (38ms/3.2% longer)\n-   `text-wrap: pretty`: 1,310ms (124ms/10.5% longer)\n\nHe said that impact on `balance` was almost zero, while `pretty` is more noticeable. The reality is that you most likely won’t have 10,000 paragraphs on your page.\n\nThe properties so far are only configuring how the whitespace characters behave in a block of text. None of these properties will affect the strings of characters that typically behave as words. The following properties are meant to target when these strings are meant to break and avoid poor layout artifacts.\n\n### `overflow-wrap`\n\nThe `overflow-wrap` property helps tell the browser when it should insert line breaks in otherwise unbreakable strings of text to prevent overflowing. This is an alias of the `word-wrap` property. Originally, this only allowed two values `normal` the default which keeps strings in tact and `break-word` which will break words *sometimes*. When a word is meant to break depends on the width of the container and where soft breaks can exist. This is usually very helpful for places where a long URL is written that needs to be broken to maintain a reasonable text layout.\n\nThe newer value option is `anywhere` which is typically less desirable as soft wrap characters are also used to determine how big the line box should be. This will allow the box to be much smaller instead of trying to fill the box as is normally expected.\n\n### `word-break`\n\nContinuing with the theme of confusingly similar CSS properties, `word-break` is a property that determines what happens when a string would overflow outside of its container. To make things worse, one of the values here is an override to `overflow-wrap`. We’ll start with that one:\n\n-   `break-word` is identical to `overflow-wrap: anywhere` and takes priority over any other value provided to `overflow-wrap`.\n-   `normal` says to use normal line break rules which may also break between CJK characters.\n-   `break-all` will cause a break to occur at the place where overflow would occur.\n-   `keep-all` same as `normal` except CJK characters will also not break.\n-   `auto-phrase` same as `normal` but analysizes the language to avoid breaking natural phrases.\n\nA question you might have, what’s the difference between all of these properties? They all look like they handle the same sort of consideration for breaking strings of text. The CSSWG has an [excellent breakdown of the differences](https://drafts.csswg.org/css-text/#line-breaking):\n\n> -   The `line-break` property allows choosing various levels of “strictness” for line breaking restrictions.\n> -   The `word-break` property controls what types of letters are glommed together to form unbreakable “words”, causing CJK characters to behave like non-CJK text or vice versa.\n> -   The `hyphens` property controls whether automatic hyphenation is allowed to break words in scripts that hyphenate.\n> -   The `overflow-wrap` property allows the user-agent to take a break anywhere in otherwise-unbreakable strings that would otherwise overflow.\n\nOn top of the other properties that we didn’t cover, there’s a lot more detail within that page about how line breaks are expected to work. I’ve made a little playground to see how the properties would affect text samples combined from MDN examples:\n\n<p>See the Pen <a href=\"https://codepen.io/fauxserious/pen/jEbNMge\"> Antidatalossconfigurationism</a> by Donnie D’Amato (<a href=\"https://codepen.io/fauxserious\">@fauxserious</a>) on <a href=\"https://codepen.io\">CodePen</a>.</p>\n\n## Recommendations\n\nBased on all of this, here’s how I’d try making an API for a text component when it comes to avoiding data loss. Here’s my considerations:\n\n-   Create a `wrap` flag with the following values:\n    -   (default) if the tag name used expects a certain `text-wrap-style` value, use that style. For example, `balance` for shorter text and `pretty` for longer text.\n    -   `true` is the same as `text-wrap-mode: wrap`.\n    -   `false` is the same as `text-wrap-mode: nowrap`.\n-   Create a `break` flag with the following values:\n    -   (default) sets `overflow-wrap: break-word` on text by default. This will allow the presence of long URL to break nicely when within the text. [Josh Comeau](https://www.joshwcomeau.com/) includes this in [his CSS reset](https://www.joshwcomeau.com/css/custom-css-reset/).\n    -   `true` is the same as `word-break: break-all`.\n    -   `false` is the same as `word-break: keep-all`.\n\nHere’s what the CSS might look like:\n\n```\n.text {\n    overflow: visible !important;\n    overflow-wrap: break-word;\n\n    &:where(h1, h2, h3, h4, h5, h6, dt, legend, label, button, th) {\n        text-wrap: balance;\n    }\n\n    &:where(p, li, figcaption, dd, summary, caption) {\n        text-wrap: pretty;\n    }\n\n    &:where(textarea, [contenteditable]) {\n        text-wrap: stable;\n    }\n\n    &:where([data-wrap=\"true\"]):where(pre, kbd) {\n        text-wrap: pre-wrap;\n    }\n\n    &:where([data-wrap=\"false\"]) {\n        text-wrap: nowrap;\n    }\n\n    &:where([data-break=\"true\"]) {\n        word-break: break-all;\n    }\n\n    &:where([data-break=\"false\"]) {\n        word-break: keep-all;\n    }\n}\n```\n\nI’m using the keyword value for `text-wrap` over the shorthand because it’s easier to update this value instead of unsetting it to reapply in some cases. This allows us to leverage the default of `text-wrap-mode: wrap` for most elements.\n\nThe elements I’ve chosen to receive `text-wrap-style` are based on their expected content. Elements that expect shorter amounts of content receive `balance` while elements that expect longer amounts use `pretty`. Note that these are targeting elements that would normally have content as their direct children. While you could put `pretty` on sectioning content for it to cascade, it’s more performant to be more specific.\n\nIf you were paying attention, you’ll notice that there’s a lot of property values that we went over which are missing from the API. The reason is that they’re probably not going to be useful in day-to-day web development. This isn’t meant to be a CSS reset, this is an opinionated setup for what I believe would be a text component that allows for some customizability while keeping the options limited.\n\nThis includes my own rule-breaking for use of `!important` in this case to call out that hiding overflow on text is a bad practice. [Makes me want to set `z-index: auto !important`](/posts/out-of-order/) on [all my elements](/posts/hottest-box/) too.",
    "version": "1.0"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreidprl56mrrexfe32ii2ujziycbqqjzdhwvb3hdk4zcuxr5m5hs3fa"
    },
    "mimeType": "image/png",
    "size": 295537
  },
  "description": "Wrangling text and the CSS behind it all.",
  "path": "/posts/antidatalossconfigurationism",
  "publishedAt": "2025-07-08T00:00:00.000Z",
  "site": "https://blog.damato.design",
  "tags": [
    "typography",
    "css"
  ],
  "textContent": "I’m in the middle of deep thought about what a text component looks like. There’s the easy stuff like having the ability to choose the appropriate semantic element. There’s the less obvious stuff, like adding a flag for screenreader only content. But then there’s something that we tend to overlook, how to manage overflow content.\n\nData loss\n\nIt’s not uncommon for someone to choose the solution of clipping the content, maybe in the form of an ellipsis. We even have more options coming about when those ellipsis appear. The problem with approaches that include  is that the content is no longer clearly accessible. This goes against a core principle of mine: if you place it on the page, that means it is important. There should be no condition that obstructs that element in any way. Otherwise that can introduce data loss. Adding ellipsis is not an inclusive solution to large blocks of text. Instead, we’ll need to ensure the text remains readable while balancing the expectations of the layout.\n\nProperties\n\nThere’s several CSS properties that are directly related to the way text flows on a webpage. We’ll go over each of the properties below but first a few definitions:\nWhitespace refers to characters that provide space between other characters.\nSegment breaks are characters that cause text to break onto new lines (eg., line feed).\nHard breaks (aka: forced line break) will always cause the text to break onto a new line, even if it is not necessary to do so.\nSoft breaks will cause text to break to a new line only if necessary. Where these can occur are called soft wrap opportunities.\nLine box describes the inline formatted box, typically used to contain text. The size of this box depends on the content and the available size of its ancestors.\nCJK is short for Chinese/Japanese/Korean used to describe text that has a different ruleset for when strings are allowed to break.\n\nThis property has been around for a while, but recently it has been altered to host separate properties. Overall, this property and its related new properties are meant to handle how whitespace characters effect the surrounding text.\n\nFirst, the  property has the following possible values:\nsays that all whitespace should be collapsed. How whitespace is collapsed follows several algorithmic steps.\nsays whitespace and segment break characters are perserved. This is commonly seen as the default in the  HTML element style.\nsays only preserve segment breaks, whitespace characters are collapsed.\ndoes the opposite of  by allowing segment breaks to be collapsed and perserve whitespace as written in source.\nis the most unique of the values with a more specific set of rules for how whitespace is perserved.\n\nRemember that  is only one part of the  property. The other part is  which only has two values,  and . As you might expect, you’ll primarly want to have  and this is the default.\n\nWhen considering the keyword options provided by the original  property, it’s not immediately clear how they align to these values:\nis .\nis .\nis .\nis .\n\nThere is no keyword analog under  to get  behavior. At the time of this writing, this is only available in Firefox.\n\nTo make things more confusing,  is also a shorthand. We already saw one of the properties  which determines if the text is allowed to wrap or not. The newer part to this is  which has added many of the fancy algorithmic ways that text can wrap based on the number of characters. Here are the values for this property:\nwraps the text in the most performant way and ignores the number of characters. This is the default.\nattempts to wrap in away that keeps the number of characters roughly equal amongst the lines of text. This is best used for headlines.\nis similar to balance but favors layout over speed. This is best used for paragraphs of text.\nis meant to be applied in areas where the user is editing content and wrapping should be kept to a minimum during this time.\n\nHarry Roberts posted some benchmarks on using the newer  values. The setup was a page with 10,000  elements which, by default, have orphans. Here’s what he found:\nBaseline – 1,186ms\n: 1,224ms (38ms/3.2% longer)\n: 1,310ms (124ms/10.5% longer)\n\nHe said that impact on  was almost zero, while  is more noticeable. The reality is that you most likely won’t have 10,000 paragraphs on your page.\n\nThe properties so far are only configuring how the whitespace characters behave in a block of text. None of these properties will affect the strings of characters that typically behave as words. The following properties are meant to target when these strings are meant to break and avoid poor layout artifacts.\n\nThe  property helps tell the browser when it should insert line breaks in otherwise unbreakable strings of text to prevent overflowing. This is an alias of the  property. Originally, this only allowed two values  the default which keeps strings in tact and  which will break words sometimes. When a word is meant to break depends on the width of the container and where soft breaks can exist. This is usually very helpful for places where a long URL is written that needs to be broken to maintain a reasonable text layout.\n\nThe newer value option is  which is typically less desirable as soft wrap characters are also used to determine how big the line box should be. This will allow the box to be much smaller instead of trying to fill the box as is normally expected.\n\nContinuing with the theme of confusingly similar CSS properties,  is a property that determines what happens when a string would overflow outside of its container. To make things worse, one of the values here is an override to . We’ll start with that one:\nis identical to  and takes priority over any other value provided to .\nsays to use normal line break rules which may also break between CJK characters.\nwill cause a break to occur at the place where overflow would occur.\nsame as  except CJK characters will also not break.\nsame as  but analysizes the language to avoid breaking natural phrases.\n\nA question you might have, what’s the difference between all of these properties? They all look like they handle the same sort of consideration for breaking strings of text. The CSSWG has an excellent breakdown of the differences:\nThe  property allows choosing various levels of “strictness” for line breaking restrictions.\nThe  property controls what types of letters are glommed together to form unbreakable “words”, causing CJK characters to behave like non-CJK text or vice versa.\nThe  property controls whether automatic hyphenation is allowed to break words in scripts that hyphenate.\nThe  property allows the user-agent to take a break anywhere in otherwise-unbreakable strings that would otherwise overflow.\n\nOn top of the other properties that we didn’t cover, there’s a lot more detail within that page about how line breaks are expected to work. I’ve made a little playground to see how the properties would affect text samples combined from MDN examples:\n\nSee the Pen  Antidatalossconfigurationism by Donnie D’Amato (@fauxserious) on CodePen.\n\nRecommendations\n\nBased on all of this, here’s how I’d try making an API for a text component when it comes to avoiding data loss. Here’s my considerations:\nCreate a  flag with the following values:\n(default) if the tag name used expects a certain  value, use that style. For example,  for shorter text and  for longer text.\nis the same as .\nis the same as .\nCreate a  flag with the following values:\n(default) sets  on text by default. This will allow the presence of long URL to break nicely when within the text. Josh Comeau includes this in his CSS reset.\nis the same as .\nis the same as .\n\nHere’s what the CSS might look like:\n\nI’m using the keyword value for  over the shorthand because it’s easier to update this value instead of unsetting it to reapply in some cases. This allows us to leverage the default of  for most elements.\n\nThe elements I’ve chosen to receive  are based on their expected content. Elements that expect shorter amounts of content receive  while elements that expect longer amounts use . Note that these are targeting elements that would normally have content as their direct children. While you could put  on sectioning content for it to cascade, it’s more performant to be more specific.\n\nIf you were paying attention, you’ll notice that there’s a lot of property values that we went over which are missing from the API. The reason is that they’re probably not going to be useful in day-to-day web development. This isn’t meant to be a CSS reset, this is an opinionated setup for what I believe would be a text component that allows for some customizability while keeping the options limited.\n\nThis includes my own rule-breaking for use of  in this case to call out that hiding overflow on text is a bad practice. Makes me want to set  on all my elements too.",
  "title": "Antidatalossconfigurationism",
  "updatedAt": "2026-06-13T19:13:38.945Z"
}