{
  "$type": "site.standard.document",
  "content": {
    "$type": "site.standard.content.markdown",
    "text": "Over the past year, I’ve had the task of creating layout components for an organization. This is something that was initially confused about. My initial assumption was that everyone knows about flexbox! It’s our lord and layout savior! Jokes about centering content should have vanished away now that we can use flexbox! Oh, how naïve I was.\n\nRealistically, the developer landscape doesn’t hone these skills well, and instead hides them behind APIs; even if [those very APIs](https://tailwindcss.com/docs/flex-direction) are identical to what a person would write in CSS. It’s true, [people will do anything to avoid writing code in a `.css` file](https://x.com/donniedamato/status/1831662438191612281).\n\nHonestly, I stumbled all over making a robust layout component. The following is considerations I take into account and how I might build a new component if I was given another opportunity.\n\n## Layout\n\nThe first thing I’d take into consideration is layout using flexbox. I’m avoiding introducing grid configurations here. I’ve found that in the years I’ve been working with CSS, grid is the lesser used layout technique. It certainly has benefits over flexbox in many cases, but the majority of layout needs revolve around aligning items on a single axis in a container.\n\nMuch of the confusion about flexbox comes from the alignment properties; `align-items` and `justify-content`. They are very easy to get mixed up and, the results can be unexpected when you change the `flex-direction`. I believe an improvement here would be to simplify the API.\n\nI’ll be using JSX syntax to describe the component API, but you could imagine similar configurations in your authoring experience of choice.\n\n```\n<Box stretch stack>Hello world!</Box>\n```\n\nThe first thing I’d introduce is the boolean properties of `stretch` and `stack`. The `stretch` flag would switch between `inline-flex` and `flex` so the container can fill its parent. The `stack` flag would toggle on `flex-direction: column` so the children stack in a single column. I’d also include `wrap` to align directly to `flex-wrap: wrap`.\n\nWith that out of the way, now we need to address the alignment properties. Generally, the expectation for someone manipulating layout is to place the content in one of 9 locations within the container. For example, take the following CSS:\n\n```\n.box {\n    display: flex;\n    align-items: start;\n    justify-content: end;\n}\n```\n\nThis code will place the content in the top-right corner of an LTR container. But with one line of code, it’ll completely flip the position to the bottom-left corner:\n\n```\n.box {\n    display: flex;\n+   flex-direction: column;\n    align-items: start;\n    justify-content: end;\n}\n```\n\nIn my opinion, this is unexpected. The intention to show the items in a row versus a column shouldn’t have an effect on the alignment in this case. Instead, I’d like to see an approach where someone could configure the items such that the `stack` has no effect.\n\nI’m aware that there are cases where we want the above behavior, such as when the writing mode is adjusted. I’d argue that this is rare in comparison to the task of creating layouts.\n\nFurthermore, I’d like to lean into [CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values), such that configuration inputs have aliases; `left` / `top` would transform to `start` and `right` / `bottom` transforms to `end`, of course allowing these values directly as well. I could imagine some flag that turns off (eg., `logical=false`) this translation [in cases where we want to force the direction regardless of the writing mode](https://m2.material.io/design/usability/bidirectionality.html#mirroring-elements).\n\n```\n<Box logical={ false }>\n    Media player timeline\n</Box>\n```\n\nImportantly, we’d need to inform what axis to apply the `start` or `end`. Logical properties describe that the horizontal axis is the “inline” direction, and the vertical axis is the “block” direction. As we have seen, when we update the `flex-direction`, these axes flip where I’d argue this is largely unexpected. So instead of using `align` and `justify`, I’d suggest `inline` and `block`, similar to how `padding` and `margin` would be modernly applied.\n\nWith this all in mind, we’d have the following:\n\n```\n<Box\n    stack\n    inline=\"left\"\n    block=\"bottom\">\n    Hello world!\n</Box>\n```\n\nRegardless if the `stack` was applied or not, the children of this LTR container would be placed in the bottom-left corner. In an RTL container, the items would be placed in the bottom-right corner because `logical` defaults to `true`.\n\nThe final element to consider here is handling the concept of “self”. The flexbox ecosystem allows an item to essentially override the placement given by the parent through properties such as `align-self` and `justify-self`. Knowing this, we can also see a potential problem with the current API. The words `inline` and `block` aren’t clear as to what they are affecting. A better API naming convention would be more clear about what these are doing. That is why the flexbox APIs include the words `items` and `content`; they are affecting the elements *inside* this container, not the container itself.\n\nThis means that we should have similar but separate APIs for the concept of affecting the elements inside versus the element itself. We can communicate that layout is affecting the interior children with `inset` (ie., interior setup) and the exterior self with `outset`:\n\n```\n<Box\n    inset={{ inline: 'start', block: 'end' }}\n    outset={{ /* Similar options */ }}>\n    Hello world!\n</Box>\n```\n\nWe could also consider shorthands that handle these with a single value; affecting both `inline` and `block` in the same way. The following would place the text horizontally and vertically within the box:\n\n```\n<Box inset='center'>\n    Hello world!\n</Box>\n```\n\n`inset` means [something slightly different in CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/inset) compared to how it is being used here. In CSS, the “in” is speaking about how the target is being set inside a parent element.\n\nOne more thing to mention is the more nuianced alignment properties, such as `justify-content: space-between`. Since we’re hiding implementation details to match some new expectations, I’d consider an option that would effectively override the given `inline` or `block` (depending on the `stack` flag) to distribute the children.\n\n```\n<Box stack inset=\"center\" distribute=\"between\">\n    <Box>Child 1</Box>\n    <Box>Child 2</Box>\n    <Box>Child 3</Box>\n</Box>\n```\n\nIn this example, the `justify-content` would be set as `space-between` instead of `center`. I’m avoiding the word “space” here to not confuse with other spacing properties such as `padding` or `gap` which we’ll introduce later.\n\n## Appearance\n\nBy default, it makes sense to allow this container to be invisible when a configuration is omitted. This helps with general layout approaches, allowing boxes to be arranged without explicitly showing boundaries. However, in some cases we may want to have a box appear segregated from other boxes.\n\nAligning with the idea of [Mise en Mode](https://mode.place), appearance properties would be informed by the use of intents (ie., strict semantic tokens) where we’d consider the priority that this box is meant to convey. So the box with the highest priority would be presented in a way that was clear to a user that they should pay attention here first before looking at other elements. This suggests that there is a `priority` setting that could either be numeric (`1`, `2`, `3`, and so on) or Latinate ordinal numbers (`primary`, `secondary`, etc.). I’d recommend the latter and having only `default`, `secondary`, and `primary`. This reduces the decision-making paralysis; making it more clear which to use. Having a number system of significant size makes it challenging to determine the most appropriate number. I also suggest using the term `default` over including `tertiary` as if you need an additional level, you can include it later.\n\nAs a sidenote, you should never assign the primary concept of anything as the default. Primary should be reserved as the main focus for the user. Meanwhile, your default should be set as the most common presentation of any element. If primary is your most common presentation, that means nothing is really important because everything is important.\n\nContinuing with the Mise en Mode concept, this allows any box to present itself with any color combination as provided by the current mode. This means, if you need a purple gradient to convey the supreme intelligence of AI, you can do it by informing the intents with new values, not by providing new tokens directly to a component.\n\nI believe the priority of any box is more closely tied to the concept of surfaces. We could imagine that the `default` priority to be the base level of the page where most UI elements would live. Next, the `secondary` level could be reserved for elements that are meant to appear on top of the UI but are contextual to some anchor. You can think of flyouts like context menus or tooltips. These are introduced because there is some more important information for you to take in before continuing, but they aren’t typically the most important thing to do. Finally, the `primary` level would be a full disruption like a modal, which is meant to appear on top of everything blocking further progress.\n\n```\n<Box aria-modal=\"true\" inset=\"center\" priority=\"primary\">\n   Hello world!\n</Box>\n```\n\nAny one of these priorities would apply additional presentational styles to the element to allow it to convey the priority consistently. This is so our users can subconsciously learn the hierarchical order of the content and make better decisions.\n\n## Spacing\n\nContinuing with Mise en Mode, more specifically its origins of [Complementary Space](https://complementary.space/), I believe we’ll only need two spacing options for box: `padding` and `gap`. These would be *flags* and not expecting values like T-shirt sizing. This is because the mode would inform the size.\n\n```\n/* Example 1 */\n<Box padding gap>\n    <Box>Child 1</Box>\n    <Box>Child 2</Box>\n</Box>\n\n/* Example 2 */\n<Box padding gap data-mode=\"density-shift\">\n    <Box>Child 1</Box>\n    <Box>Child 2</Box>\n</Box>\n```\n\nThe `data-mode` in the second example indicates that a mode changes occurs here. This would affect all intents of this element and below new values. The `density-shift` value would refer to a mode that expects the density to shift down one level, such that `padding` and `gap` are smaller than if this mode wasn’t applied; as in the first example.\n\nYou might consider simply adding `mode` as the option instead. I’m specifically using `data-mode` to indicate that this isn’t specific to `<Box/>` but more related to Mise en Mode. If this element is meant to be the base of all other elements, then `mode` could be appropriate.\n\nIf Complementary Space is too radical of an idea, feel free to wade in the depths of applying T-shirt sizing values.\n\n## Loading\n\nSome work that was being done in parallel to the layout component was that of a skeleton loader. [For many systems](https://component.gallery/components/skeleton/), this is a separate component than any others in the system for the specific purpose of displaying a placeholder while work is done in the background. This has the perception that the system is processing, as opposed to having a blank area waiting for content to appear.\n\nIt was at this time that I realized that if the skeleton component represented content, and the box component was meant to arrange content, what if the box could represent placeholder content until it was provided? In other words, we can mark `<Box/>` elements with an attribute that shows a skeleton loading presentation if the element has no content. Consider the following simple card layout:\n\n```\n<Box stack padding gap>\n    <Media src={ src } />\n    <Box stack padding gap data-mode=\"density-shift\">\n        <Box standby>{ title }</Box>\n        <Box standby>{ description }</Box>\n        <Box standby>{ actions }</Box>\n    </Box>\n</Box>\n```\n\nThe `standby` flag would show a skeleton loading presentation to elements if no content is provided. That could be done with the CSS `:empty` selector.\n\n```\n[data-standby]:empty {\n    /* Skeleton loading presentation */\n}\n```\n\nOnce content is added, the selector would no longer match and the skeleton styles would be removed through CSS. We could also consider being explicit about when the loader should be shown by using the value of `data-standby=\"true\"`.\n\nIn a more complete system, there would exist `<Media/>` and `<Text/>` components that would have more specific enhancements that aligned better for `standby`, most likely inheriting from the foundational `<Box/>`. For example, `<Media/>` would have loading when no `src` is provided (`:not([src])`). `<Text/>` would display skeleton lines instead of a single block, depending on the purpose of text. Titles could display a single line, while paragraphs could show 3.\n\nThe purpose of embedding the ability to display a skeleton in any layout is powerful; assuming performance cannot be improved. This means that you can have a layout reused between the expected result and the lack of information with a single composition; just add content. No need to create a separate layout for when the content is missing and another for when the content is finally resolved.\n\n## Semantics\n\nSomething I’ve glossed over is how to assign the element that this component represents. In some systems, this is done with another prop that informs the HTML tag name to use (`<Box as=\"section\"/>`). Instead, I’d recommend trying [a different approach](https://github.com/framer/motion/blob/main/packages/framer-motion/src/components/utils/tag-proxy.ts#L16-L32).\n\n```\n<box.section>\n    Hello world!\n</box.section>\n```\n\nIn this way, it is more clear that the `section` part of the element is related to the element that will be rendered. Using `as` could be more ambiguous to what the prop is meant to do, such as the word `variant`. You could also consider a more generic default export as `Box` that refers to a simple `box.div`.\n\n## Demo\n\nHere’s a demo of the concept, missing all the mode changing.\n\n<p>See the Pen <a href=\"https://codepen.io/fauxserious/pen/PorvMEy\"> The Hottest Box</a> by Donnie D’Amato (<a href=\"https://codepen.io/fauxserious\">@fauxserious</a>) on <a href=\"https://codepen.io\">CodePen</a>.</p>\n\n---\n\n**EDIT (2024-09-26):** [Devon Govett recently posted a similar approach](https://x.com/devongovett/status/1838980741197447529) where a `<Skeleton/>` wrapper is added to *any* component in the system which will cause media and text elements inside to shimmer. Based on what I’ve written above, of course I really resonate with this. I like how the wrapping component is the trigger because you can also add [accessibility attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy) in there to markup the loading state within.\n\nThere’s some feedback in the replies that challenges how the system is supposed to know what the content looks like. The fact is that we don’t need to know what the content looks like, we need to know what *kind* of content it is.\n\nThis is why thinking about design as intention over aesthetics is the key to systems thinking and how we could support this approach. For example, the intention for a heading is commonly for introducing a short concept or label. Therefore, the loading presentation for the area where a heading should appear could always be displayed as a single block instead of considering several lines. Certainly, it is entirely possible that the final content does create multiple lines however, the purpose of the skeleton is not to match one-for-one with the incoming content. It is meant to indicate perceived progress using a loose blueprint of the expected content.\n\nWe already know the styles for the text that will appear once the content comes through, including the font size. We can use that font size to set the minimum height of the skeleton placeholder, perhaps using the new `lh` unit. Also know that any styles that we use for the skeleton do not need to remain for when the content is applied. If the `lh` unit isn’t appropriate for the incoming font, then no need to set a minimum height when the element is `:not(:empty)`.\n\nFor paragraphs, you can pick a number of lines to display when we expect a paragraph. You could even be more considerate and wire in a container query that changes the number of lines based on the size of the paragraph. Wider paragraph containers larger than `60ch` in width can display 2 lines, while smaller widths can display 3. This could imitate line wrapping for the same repeatable placeholder content. This way, a paragraph in a loading hero section would show 2 lines, while a more narrow card would show 3 lines.\n\nThe easiest one of these content elements to consider is the image or really any media. [Best practice for loading media](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/) is to set the dimensions in the HTML to help with content shifts which would clearly dictate the size of the container for the skeleton. In the event you couldn’t do this, you could have predetermined expectations for that media; square, 4:3, 16:9, and even a circle for avatars. These styles are already most likely applied to the media element already, so we shouldn’t need to use too much imagination about how this skeleton might look.",
    "version": "1.0"
  },
  "description": "Considerations to take when making a layout component.",
  "path": "/posts/hottest-box",
  "publishedAt": "2024-09-16T00:00:00.000Z",
  "site": "https://blog.damato.design",
  "tags": [
    "components",
    "css",
    "space"
  ],
  "textContent": "Over the past year, I’ve had the task of creating layout components for an organization. This is something that was initially confused about. My initial assumption was that everyone knows about flexbox! It’s our lord and layout savior! Jokes about centering content should have vanished away now that we can use flexbox! Oh, how naïve I was.\n\nRealistically, the developer landscape doesn’t hone these skills well, and instead hides them behind APIs; even if those very APIs are identical to what a person would write in CSS. It’s true, people will do anything to avoid writing code in a  file.\n\nHonestly, I stumbled all over making a robust layout component. The following is considerations I take into account and how I might build a new component if I was given another opportunity.\n\nLayout\n\nThe first thing I’d take into consideration is layout using flexbox. I’m avoiding introducing grid configurations here. I’ve found that in the years I’ve been working with CSS, grid is the lesser used layout technique. It certainly has benefits over flexbox in many cases, but the majority of layout needs revolve around aligning items on a single axis in a container.\n\nMuch of the confusion about flexbox comes from the alignment properties;  and . They are very easy to get mixed up and, the results can be unexpected when you change the . I believe an improvement here would be to simplify the API.\n\nI’ll be using JSX syntax to describe the component API, but you could imagine similar configurations in your authoring experience of choice.\n\nThe first thing I’d introduce is the boolean properties of  and . The  flag would switch between  and  so the container can fill its parent. The  flag would toggle on  so the children stack in a single column. I’d also include  to align directly to .\n\nWith that out of the way, now we need to address the alignment properties. Generally, the expectation for someone manipulating layout is to place the content in one of 9 locations within the container. For example, take the following CSS:\n\nThis code will place the content in the top-right corner of an LTR container. But with one line of code, it’ll completely flip the position to the bottom-left corner:\n\nIn my opinion, this is unexpected. The intention to show the items in a row versus a column shouldn’t have an effect on the alignment in this case. Instead, I’d like to see an approach where someone could configure the items such that the  has no effect.\n\nI’m aware that there are cases where we want the above behavior, such as when the writing mode is adjusted. I’d argue that this is rare in comparison to the task of creating layouts.\n\nFurthermore, I’d like to lean into CSS Logical Properties, such that configuration inputs have aliases;  /  would transform to  and  /  transforms to , of course allowing these values directly as well. I could imagine some flag that turns off (eg., ) this translation in cases where we want to force the direction regardless of the writing mode.\n\nImportantly, we’d need to inform what axis to apply the  or . Logical properties describe that the horizontal axis is the “inline” direction, and the vertical axis is the “block” direction. As we have seen, when we update the , these axes flip where I’d argue this is largely unexpected. So instead of using  and , I’d suggest  and , similar to how  and  would be modernly applied.\n\nWith this all in mind, we’d have the following:\n\nRegardless if the  was applied or not, the children of this LTR container would be placed in the bottom-left corner. In an RTL container, the items would be placed in the bottom-right corner because  defaults to .\n\nThe final element to consider here is handling the concept of “self”. The flexbox ecosystem allows an item to essentially override the placement given by the parent through properties such as  and . Knowing this, we can also see a potential problem with the current API. The words  and  aren’t clear as to what they are affecting. A better API naming convention would be more clear about what these are doing. That is why the flexbox APIs include the words  and ; they are affecting the elements inside this container, not the container itself.\n\nThis means that we should have similar but separate APIs for the concept of affecting the elements inside versus the element itself. We can communicate that layout is affecting the interior children with  (ie., interior setup) and the exterior self with :\n\nWe could also consider shorthands that handle these with a single value; affecting both  and  in the same way. The following would place the text horizontally and vertically within the box:\n\n means something slightly different in CSS compared to how it is being used here. In CSS, the “in” is speaking about how the target is being set inside a parent element.\n\nOne more thing to mention is the more nuianced alignment properties, such as . Since we’re hiding implementation details to match some new expectations, I’d consider an option that would effectively override the given  or  (depending on the  flag) to distribute the children.\n\nIn this example, the  would be set as  instead of . I’m avoiding the word “space” here to not confuse with other spacing properties such as  or  which we’ll introduce later.\n\nAppearance\n\nBy default, it makes sense to allow this container to be invisible when a configuration is omitted. This helps with general layout approaches, allowing boxes to be arranged without explicitly showing boundaries. However, in some cases we may want to have a box appear segregated from other boxes.\n\nAligning with the idea of Mise en Mode, appearance properties would be informed by the use of intents (ie., strict semantic tokens) where we’d consider the priority that this box is meant to convey. So the box with the highest priority would be presented in a way that was clear to a user that they should pay attention here first before looking at other elements. This suggests that there is a  setting that could either be numeric (, , , and so on) or Latinate ordinal numbers (, , etc.). I’d recommend the latter and having only , , and . This reduces the decision-making paralysis; making it more clear which to use. Having a number system of significant size makes it challenging to determine the most appropriate number. I also suggest using the term  over including  as if you need an additional level, you can include it later.\n\nAs a sidenote, you should never assign the primary concept of anything as the default. Primary should be reserved as the main focus for the user. Meanwhile, your default should be set as the most common presentation of any element. If primary is your most common presentation, that means nothing is really important because everything is important.\n\nContinuing with the Mise en Mode concept, this allows any box to present itself with any color combination as provided by the current mode. This means, if you need a purple gradient to convey the supreme intelligence of AI, you can do it by informing the intents with new values, not by providing new tokens directly to a component.\n\nI believe the priority of any box is more closely tied to the concept of surfaces. We could imagine that the  priority to be the base level of the page where most UI elements would live. Next, the  level could be reserved for elements that are meant to appear on top of the UI but are contextual to some anchor. You can think of flyouts like context menus or tooltips. These are introduced because there is some more important information for you to take in before continuing, but they aren’t typically the most important thing to do. Finally, the  level would be a full disruption like a modal, which is meant to appear on top of everything blocking further progress.\n\nAny one of these priorities would apply additional presentational styles to the element to allow it to convey the priority consistently. This is so our users can subconsciously learn the hierarchical order of the content and make better decisions.\n\nSpacing\n\nContinuing with Mise en Mode, more specifically its origins of Complementary Space, I believe we’ll only need two spacing options for box:  and . These would be flags and not expecting values like T-shirt sizing. This is because the mode would inform the size.\n\nThe  in the second example indicates that a mode changes occurs here. This would affect all intents of this element and below new values. The  value would refer to a mode that expects the density to shift down one level, such that  and  are smaller than if this mode wasn’t applied; as in the first example.\n\nYou might consider simply adding  as the option instead. I’m specifically using  to indicate that this isn’t specific to  but more related to Mise en Mode. If this element is meant to be the base of all other elements, then  could be appropriate.\n\nIf Complementary Space is too radical of an idea, feel free to wade in the depths of applying T-shirt sizing values.\n\nLoading\n\nSome work that was being done in parallel to the layout component was that of a skeleton loader. For many systems, this is a separate component than any others in the system for the specific purpose of displaying a placeholder while work is done in the background. This has the perception that the system is processing, as opposed to having a blank area waiting for content to appear.\n\nIt was at this time that I realized that if the skeleton component represented content, and the box component was meant to arrange content, what if the box could represent placeholder content until it was provided? In other words, we can mark  elements with an attribute that shows a skeleton loading presentation if the element has no content. Consider the following simple card layout:\n\nThe  flag would show a skeleton loading presentation to elements if no content is provided. That could be done with the CSS  selector.\n\nOnce content is added, the selector would no longer match and the skeleton styles would be removed through CSS. We could also consider being explicit about when the loader should be shown by using the value of .\n\nIn a more complete system, there would exist  and  components that would have more specific enhancements that aligned better for , most likely inheriting from the foundational . For example,  would have loading when no  is provided ().  would display skeleton lines instead of a single block, depending on the purpose of text. Titles could display a single line, while paragraphs could show 3.\n\nThe purpose of embedding the ability to display a skeleton in any layout is powerful; assuming performance cannot be improved. This means that you can have a layout reused between the expected result and the lack of information with a single composition; just add content. No need to create a separate layout for when the content is missing and another for when the content is finally resolved.\n\nSemantics\n\nSomething I’ve glossed over is how to assign the element that this component represents. In some systems, this is done with another prop that informs the HTML tag name to use (). Instead, I’d recommend trying a different approach.\n\nIn this way, it is more clear that the  part of the element is related to the element that will be rendered. Using  could be more ambiguous to what the prop is meant to do, such as the word . You could also consider a more generic default export as  that refers to a simple .\n\nDemo\n\nHere’s a demo of the concept, missing all the mode changing.\n\nSee the Pen  The Hottest Box by Donnie D’Amato (@fauxserious) on CodePen.\n\nEDIT (2024-09-26): Devon Govett recently posted a similar approach where a  wrapper is added to any component in the system which will cause media and text elements inside to shimmer. Based on what I’ve written above, of course I really resonate with this. I like how the wrapping component is the trigger because you can also add accessibility attributes in there to markup the loading state within.\n\nThere’s some feedback in the replies that challenges how the system is supposed to know what the content looks like. The fact is that we don’t need to know what the content looks like, we need to know what kind of content it is.\n\nThis is why thinking about design as intention over aesthetics is the key to systems thinking and how we could support this approach. For example, the intention for a heading is commonly for introducing a short concept or label. Therefore, the loading presentation for the area where a heading should appear could always be displayed as a single block instead of considering several lines. Certainly, it is entirely possible that the final content does create multiple lines however, the purpose of the skeleton is not to match one-for-one with the incoming content. It is meant to indicate perceived progress using a loose blueprint of the expected content.\n\nWe already know the styles for the text that will appear once the content comes through, including the font size. We can use that font size to set the minimum height of the skeleton placeholder, perhaps using the new  unit. Also know that any styles that we use for the skeleton do not need to remain for when the content is applied. If the  unit isn’t appropriate for the incoming font, then no need to set a minimum height when the element is .\n\nFor paragraphs, you can pick a number of lines to display when we expect a paragraph. You could even be more considerate and wire in a container query that changes the number of lines based on the size of the paragraph. Wider paragraph containers larger than  in width can display 2 lines, while smaller widths can display 3. This could imitate line wrapping for the same repeatable placeholder content. This way, a paragraph in a loading hero section would show 2 lines, while a more narrow card would show 3 lines.\n\nThe easiest one of these content elements to consider is the image or really any media. Best practice for loading media is to set the dimensions in the HTML to help with content shifts which would clearly dictate the size of the container for the skeleton. In the event you couldn’t do this, you could have predetermined expectations for that media; square, 4:3, 16:9, and even a circle for avatars. These styles are already most likely applied to the media element already, so we shouldn’t need to use too much imagination about how this skeleton might look.",
  "title": "The Hottest Box",
  "updatedAt": "2026-06-13T16:19:08.286Z"
}