Search code examples
cssflexboxscalingcss-transforms

CSS scale html content in flexbox


For examples of a javascript attempt see end of question

I am trying to create the illusion of working on an A4 document inside of an HTML application and would like to scale my A4 pages to their parent element's size using only CSS. When the parent element gets smaller because (for example) the browser is scaled I want the content to remain with the same proportions but smaller, scaled to fit the parent.

enter image description here

A pure CSS solution would have my preference, but if there is no other way I wouldn't mind using JavaScript to measure the parent's dimensions and scale the child to fit.

Example code in JS:

    var page = document.getElementsByClassName("page")[0];
    var scaleX = page.parentElement.clientWidth / page.clientWidth;
    
    if (scaleX < 1) {
      page.setAttribute("style", `transform:scale(${scaleX});`);
    }

My styling currently is the following:

  .page-container {
    height: 7cm;
    width: 6cm;
    border: 1px solid black;
    overflow-x: hidden;
    overflow-y: auto;
    padding: 1rem;
    background: orange;
    display: flex;
    flex-direction: column;
    resize: both;
    align-items: center;
    justify-content: flex-start;
    align-content: flex-start;
  }
  .page {
    min-height: 29.7cm !important;
    max-height: 29.7cm !important;
    height: 29.7cm !important;
    width: 21cm !important;
    background: white;
    padding: 21mm;
    border: 1px solid gray;
  }

An example of the JS version can be found here, I can't figure out how to remove the whitespace around the page elements.

JSBin example CodeSandbox (svelte)


Solution

  • For a full CSS approach you would have to set the page container width to a percentage of the viewport. A fixed width here is not possible since you would like the container to scale when you resize the viewport.

    You then set the page width to 100% of the viewport. Calculate the page height based on the a4 ratio. Then you wrap your pages in a separate div (also set to 100% of the viewport) and scale that div back to the width of the page container by calculating the ratio between the page container and the page width.

    To make these calculations you can use CSS variables. Which you can convert later on in your CSS to viewport units using the calc() function. For instance: width: calc( var(--page-width) * 1vw );.

    Everything is now relative to each other based on the page container width you've chosen and the initial page width. And everything should scale accordingly.

    :root {
        --container-width: 35;
        --page-width: 100;
        --page-height: calc( var(--page-width) * 1.41 );
        --font-size: calc( var(--page-width) / 50 );
        --ratio: calc( var(--container-width) / var(--page-width) );
    }
    
    html {
        width: 100%;
        height: 100%; 
    }
    
    body { 
        margin: 0;
        background: silver;
        width: 100%;
        height: 100%; 
      overflow: hidden;
        padding-top: 5%;
        font-size: calc( var(--font-size) * 1vw );
    }
    
     .page-container {
        width: calc( var(--container-width) * 1vw );
        height: 90%;
        overflow-x: hidden;
        overflow-y: auto;
        margin: 0 auto;
    }
    
    .page-wrapper {
        width: calc( var(--page-width) * 1vw );
        height: 0; /* So there is no dead space after scaling */
        margin: 0;
        padding: 0;
        transform-origin: top left;
        -ms-transform: scale( var(--ratio), var(--ratio) ); /* IE 9 */
            transform: scale( var(--ratio), var(--ratio) ); /* Standard syntax */
    }
    
    .page {
        width: calc( var(--page-width) * 1vw );
        height: calc( var(--page-height) * 1vw );
        background: white;
        box-sizing: border-box;
        border: 1px solid grey;
        margin-bottom: 1vw;
        padding:2vw 4vw;
    }
    <html>
        <body>
            <div class="page-container">
                <div class="page-wrapper">
                    <div class="page">
                        <h1>Test page 1</h1>
                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
                    </div>
                    <div class="page">
                        <h1>Test page 2</h1>
                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
                    </div>
                    <div class="page">
                        <h1>Test page 3</h1>
                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
                    </div>
                </div>
            </div>
        </body>
    </html>