2

I've never read anything to suggest that the overflow property of an element would have the strange effect on element positioning that I'm seeing here:

https://codepen.io/kshetline/pen/ZEzLVxN

Toggle the toggle button in the example, and watch how somehow the background of a <div> mysteriously slides upward, covering previous content, while its contents stays in the same screen-relative place (meaning the content is moving lower relative to its parent's background).

The example is a very simplified version of something I'm trying to do with an Angular component that's meant to scale its <ng-content> — but the example is only CSS and HTML with a tiny touch of JavaScript, no Angular, since I'm trying to isolate the relevant variables.

The content of an HTML element can be scaled down using transform: scale( less-than-1 scaling factor ), but even though the content of the element is rendered smaller, by default the element's pixel dimensions remain the same, with the content (unless otherwise specified) shrinking toward the center of the element, and blank space left around that content that leaves the element at its original unscaled dimensions..

You need to compute negative margins that match the degree of scaling in order for the element itself to be considered smaller. I've done that, but I've found that unless the container for the scaled element has CSS overflow set to hidden, some weird positioning can occur, as if the extra blank space required that's supposed to be removed by the negative margins is still having some partial, hard-to-explain effect on the overall layout of other elements.

I'm seeing this behavior in Chrome, Firefox, Safari and Edge -- so I'm guessing it's "proper" CSS behavior, but it makes no sense to me, and I'm hoping someone can explain what's going on. I'd like to be able to keep overflow set to visible so that scaled content can still do things like show floating dropdown menus that don't get clipped at the boundaries of the element.

let hidden = true;
const inner = document.getElementById('inner')

function toggleOverflow() {
  hidden = !hidden;
  inner.style.overflow = hidden ? 'hidden' : 
  'visible'
}
html, body {
  height: calc(100vh - 10em);
}

.page {
  font: 32px Arial, Helvetica, sans-serif;
  height: calc(100% - 1em);
}

.container {
  background-color: #ACF;
  height: 100%;
}

.outer-wrapper {
  background-color: rgba(187, 255, 204, 0.5);
  font-size: 2em;
  margin: 0 1em;
  position: relative;
}

.inner-wrapper {
  overflow: hidden;
  position: relative;
  width: fit-content;
}

.ng-content {
  margin: -18.75px 0;
  transform: scale(0.5);
}

.container-text {
  display: inline-block;
  position: absolute;
  bottom: 1em;
}
<div class="page">
  <button onclick="toggleOverflow()">Toggle Overflow</button><br>
  Content outside of the<br>
  panel being scaled and its<br>
  containing &lt;div&gt;, 32pt font<br>
  <div class="container">
    <!--Angular component start tag goes here -->
    <div class="outer-wrapper">
      <div id="inner" class="inner-wrapper">
        <div class="ng-content">
          50% scaled content goes here, 64pt font
        </div>
      </div>
    </div>
    <!-- Angular component end tag goes here -->
    <span class="container-text">This is an absolutely positioned &lt;span&gt; in the same &lt;div&gt;</span>
  </div>
</div>
kshetline
  • 12,547
  • 4
  • 37
  • 73

3 Answers3

1

From CSS 2.2 spec

Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children.

So adding overflow:hidden is stopping the margins from collapsing.

Alohci
  • 78,296
  • 16
  • 112
  • 156
  • Thanks! This info helped lead me to a different trick for preventing margin collapse: using a very small, but non-zero amount of padding. Specifically, `padding: 0.05px`. After finding that suggestion I tried something even smaller, 0.01px, but that didn't work, so apparently at some point very small numbers effectively round down to zero. – kshetline Aug 24 '19 at 09:32
0

You have set a negative margin in your .ng-content. If overflow is set to hidden, it will hide the negative margin. Set the margin to a positive number and it will fix this jumping issue.

.ng-content { margin: 18.75px 0; }

If you are trying to change the height of the element up and down, try using max-height with overflow: hidden. When max height is set to 0, it will be hidden. When set to something like 500px, your content will show!

Everett Glovier
  • 328
  • 3
  • 15
  • Changing the margins from negative to positive would go in exactly the opposite direction of what I'm trying to achieve, which is scaling the contents of the inner element *without* getting a lot of blank space around the scaled content. – kshetline Aug 23 '19 at 21:36
0

Follow-up...

I created a variant on my first code pen here:

https://codepen.io/kshetline/pen/WNeRmOo

In this case, I'm using transform-origin: top center when I scale, and putting all of the needed negative margin on at the bottom of the scaled element, rather than splitting it evenly between top and bottom. That eliminates the weird vertical position shifting.

overflow: hidden is still needed to hide the excess of background color from "leaking out" of its container, but in the (common) case where the background of the scaled element is transparent, there would be no visible effect from using overflow: visible instead, and no worries about clipped dropdown menus originating inside the scaled element.

Follow-up #2...

Here's the best solution, using padding: 0.05px to deal with the real issue that @Alochi helped me understand — stopping border collapse:

https://codepen.io/kshetline/pen/zYONgzV

kshetline
  • 12,547
  • 4
  • 37
  • 73