diwhyyyyy
diwhyyyyy

Reputation: 6372

Floated element "steals" width from a following block element inside a display:table container

I have a div that's "floated" to the centre with display:table; margin:auto; (green box)

Inside this, I have a float:left element (red box, which is not always present), then a number of display:block; "lines" (lime boxes). The application is poetry formatting with drop capitals and reverse indentation:

https://jsfiddle.net/nktj94gd/2/

enter image description here

The problem is that the floated "L" is "stealing" its own width from the lime blocks, rather than allowing the outer block (green) to expand to prevent the lime lines wrapping when they do not have to.

I'd really like to avoid having lines wrap when they don't have to, but I cannot set the width of the overall box (it could be from a few em to 100%) .Is there a way to achieve this?

Upvotes: 0

Views: 40

Answers (2)

Alice
Alice

Reputation: 101

you probably found some more practical solution to this a year ago, but this is a fairly obscure problem I ran into while also styling poetry, so I figured I'd post my solution in case anyone else needs it :) Hope your poem project worked out, OP!

I think probably most people use <br /> and ::first-letter or something and thus never run into the problem. It's ultimately just a problem of how to preserve intended whitespace (linebreaks mainly) with a pretty dropcap, which is niche, but falls apart quick if you're designing responsively and avoiding hard breaks. (I was using whitespace: pre because it had indented stanzas) ¯\_(ツ)_/¯

How to fix OP's issue: table CSS and wrappers (responsive, markup-dependent), see: https://jsfiddle.net/eja5mnuk/

  • Stick the spans and the float in a container div
  • Change display: block on the spans to display: table-row; since your outer is display: table, and you don't impose widths anywhere here, the 'table' will simply expand to accommodate both the float and the linebox
  • Optionally, display: table-caption on an inner part of the dropcap (not truly necessary, but in OP's code so why not: https://jsfiddle.net/thn2x5zc/)
  • Everything is integrated into a table formatting context, but we aren't getting unwanted collapse that messes up sizing

This is responsive and doesn't care what your drop cap is. It runs into overflow issues if the drop cap and poem are both too big, you can shove whatever behaviour you want on this bad case if you want (like overflow: hidden on the dropcap, controlling their whitespace/widths, etc....)

It does care about your container structure and margin collapse a lot, so if your code were different, you'd need a different display value, likely something putting table-row/table-cell on a block.

Of course this does have quirks relating to how browsers treat table elements by default, though - mainly auto-collapsed borders, like in a <table>.

Method #2: ::first-letter pseudo-selector, white-space: pre, margin/padding etc. (things other people might use) see: https://jsfiddle.net/3oghpj0w/

These things are all very janky/contradictory secretly, but since ::first-letter is a pretty standard way to style drop caps, and marking up/styling your lines individually may be inconvenient, it is how I ran into the problem lol.

If it's ::first-letter, we're using a block-level element to hold the poem text. The most basic way is just to apply width: min-content; to that block's container to get it to sort of take away the float's contribution (since it's larger). Then you can add padding-right to that container to re-accommodate borders, margins, etc. (If the margin between the drop cap and poem is 3em, add 3em back to the right.)

Fixes of this style do not re-integrate the float with the poem, so the poem will still have overflow issues. But it's sufficient for fixing simple alignment problems :)

Some notes

  • The float contributes size to the block e.g. via its margins - floated margins don't collapse, generally, which is part of our problem here. But the container won't expand for things out of flow, and thus the float pushes its siblings into overflow (poetry is highly sensitive to wrapping and whitespace)
  • Ultimately, most ways the browser can calculate the size of text wrappers ignore the float (it's out of flow); everything skews towards occupying space along the inline and block axis (horizontal/vertical, here), based on everything's sizing, margins, wrapping, and other inline/block contexts, ref: https://developer.mozilla.org/en-US/docs/Web/CSS/display
  • Tables are the ancestors of most responsive text framing

Sorry this is so long! A lot of conventional fixes break when you care about the shape of the whitespace and can't treat text as a block, which makes a lot of search results very inapplicable.

Upvotes: 0

Temani Afif
Temani Afif

Reputation: 273031

I would use an extra wrapper inside that I make slightly bigger considering the width of L

.outer {
  display: table;
  margin: auto;
  overflow:auto;
  outline: 1px solid green;
}
.outer > div {
  width:calc(100% + 2.8em); /* 2.8em is an approximation of L width */
  transform:translateX(-1.4em); /* rectify the centring */
}
.dropcap {
  float: left;
}

.dropcap-inner {
  font-size: 4em;
  line-height: 1em;
  margin-right: 1em;
  border: 1px solid red;
}

.line {
  display: block;
  text-indent: -4em;
  margin-left: 4em;
  border: 1px solid lime;
}
<div class="outer">
  <div>
    <span class="dropcap"><span class="dropcap-inner">L</span></span>
    <span class="line">orem ipusm dolor sit amet</span>
    <span class="line">Lorem ipusm dolor sit amet dddd</span>
  </div>
</div>

Upvotes: 1

Related Questions