2

I have a paragraph element with some text that I have contained inside the confines of a circle by relying on the shape-outside CSS property. The HTML and CSS code thus far are as follows:

#testimony-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  height: 100%;
  text-align: center;
}

.text {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0%;
  left: 0%;
  margin: 0%;
  font-weight: 500;
}

.text::before {
  content: "";
  width: 50%;
  height: 100%;
  float: left;
  shape-outside: polygon( 0 0, 98% 0, 50% 6%, 23.4% 17.3%, 6% 32.6%, 0 50%, 6% 65.6%, 23.4% 82.7%, 50% 94%, 98% 100%, 0 100%);
  shape-margin: 7%;
}

.text p {
  height: 100%;
  margin: 0;
}

.text p::before {
  content: "";
  width: 50%;
  height: 100%;
  float: right;
  shape-outside: polygon( 2% 0%, 100% 0%, 100% 100%, 2% 100%, 50% 94%, 76.6% 82.7%, 94% 65.6%, 100% 50%, 94% 32.6%, 76.6% 17.3%, 50% 6%);
  shape-margin: 7%;
}
<div id="testimony-wrapper">
  <div class="text">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
  </div>
</div>

This is all well and good so long as the text occupies the entire height of the paragraph element. However, when there is less content, all the text is concentrated at the top of the circle. In a case like this, I would like to be able to vertically center the text inside the circle.

A lot of the solutions online suggest changing the display property to either table or flex to take advantage of the respective vertical-align and align-items properties these display types give you access to but doing so would break the shape-outside property I'm currently using to keep the content within the circle, which only works on block elements.

Is there any way to vertically align content without using either table or flex display or, if there isn't, is there a way to make the text display inside a circle without relying on the shape-outside property?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Lea
  • 33
  • 1
  • 4

2 Answers2

2

First I would consider a different idea based on this previous answer then I don't think there is a trivial way to center text with shape-outside because all the common technique will break the float porperty.

One idea is to offset the text with some padding that you calculate dynamially on load and on resize:

$('.e').css('padding-top',($('.e').parent().height() - $('.e').height())/2);
$('.e').css('padding-top',($('.e').parent().height() - $('.e').height())/2);

$(window).resize(function() {
  $('.e').css('padding-top',($('.e').parent().height() - $('.e').height())/2);
  $('.e').css('padding-top',($('.e').parent().height() - $('.e').height())/2);
});
div.box {
  background: #333;
  color:#fff;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100vmin;
  height: 100vmin;
  text-align:justify;
  border-radius: 50%;
  overflow:hidden;
}

.box >div {
  height:100%;
}

.box >div > div {
    padding-top: 50px;
}

.box:before,
.box >div:before {
  content: '';
  float: left;
  height:100%;
  width: 50%;
  shape-outside: radial-gradient(farthest-side at var(--d,right), transparent 98%, red 0);
  shape-margin:5px;
}

.box >div:before {
  float: right;
  --d:left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="box">
 <div><div class="e">Lorem ipsum dolor sit amet, eget orci, tinci dunt place rat in sociis. Pel lentes que ultri cies. cies. dolor ipsum tinci dunt place rat in sociis. Pel lentes que ultri cies. cies. dolor ipsum</div></div>
</div>

Note that I am calling the same logic twice because the height will change after the first padding adjustment and you we need another iteration to rectify. In reality we may need more iterations until it's dead center but 2 iterations seems to do the job.

I used jQuery for simplicity but you can easily translate it to vanilla JS

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
-1

I don't think it's possible in CSS yet, if you have different length of paragraph.

I created a script which helps with this, you can check the source code here: https://jsfiddle.net/jfmuydz0/

<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta charset="utf-8">
        <title>Rhombus</title>
        <style>

            body {
                display: flex;
                justify-content: center;
                align-items: center;
                flex-direction: row;
                font-family: sans-serif;
            }

            .rhombus {
                width: 640px;
                height: 640px;
                outline: 4px solid black;
                transform: scale(0.7) rotate(-45deg);
                margin: 40px 0;
                overflow: hidden;
            }

            .rhombus .inner {
                width: 100%;
                height: 100%;
                transform: scale(1.42) rotate(45deg);
            }

            .rhombus .inner .left {
                width: 50%;
                height: 100%;
                float: left;
                shape-outside: polygon(0 0, 105% 0, 5% 50%, 105% 100%, 0 100%);
            }

            .rhombus .inner .right {
                width: 50%;
                height: 100%;
                float: right;
                shape-outside: polygon(-5% 0, 100% 0, 100% 100%, -5% 100%, 95% 50%);
            }

            .rhombus .content {
                text-align: center;
                font-size: 36pt;
            }

            .rhombus .content span {
                outline: 2px dotted blue;
            }

            @media (max-width: 600px) {

                .rhombus {
                    width: 360px;
                    height: 360px;
                }

            }

        </style>
    </head>
    <body>

        <div class="rhombus">
            <div class="inner">
                <div class="left"></div><div class="right"></div>
                <div class="content">
                    <span></span>
                </div>
            </div>
        </div>

    </body>
</html>

<script>


let words = `"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.`.split(" ");

function createText() {
    let num = parseInt((Math.random() * 7) * (Math.random() * 7)) + 1;
    let str = "";
    for (let i = 0; i < num; i++) {
        let wordNum = Math.floor(Math.random() * words.length);
        str += " " + words[wordNum];
    }
    return str;
}

setInterval(() => {
    let rhombus = document.querySelector(".rhombus");
    rhombus.querySelector("span").innerHTML = createText();
    fitText(rhombus);
}, 0);

function fitText(shape) {

    let inner = shape.querySelector(".inner");

    let innerHeight = inner.offsetHeight;
    let innerHeightHalf = innerHeight / 2;
    let innerCenter = inner.getBoundingClientRect().top + innerHeightHalf;

    let content = shape.querySelector(".content");

    let span = shape.querySelector(".content span");

    const maxHeightPercentage = 60;
    const maxFontSize = innerHeight / 8;
    const minFontSize = maxFontSize / 4;

    let fontSize = maxFontSize;

    content.style.fontSize = fontSize + "px";
    content.style.padding = "0";

    for (let i = 0; i < 25; i++) {

        let spanHeightHalf = span.offsetHeight / 2;
        let padding = innerHeightHalf - spanHeightHalf;
        content.style.padding = padding + "px 0 0 0";
        let spanHeightPercent = spanHeightHalf * 200 / innerHeight;
        if (spanHeightPercent > maxHeightPercentage) {
            if (spanHeightPercent > 100) {
                if (fontSize > minFontSize + 2)
                    fontSize = fontSize - 2;    
            } else {
                if (fontSize > minFontSize + 0.5)
                    fontSize = fontSize - 0.5;
            }
            content.style.fontSize = fontSize + "px";
        }

        spanHeightHalf = span.offsetHeight / 2;
        spanHeightPercent = spanHeightHalf * 200 / innerHeight;

        let spanCenter = span.getBoundingClientRect().top + spanHeightHalf;

        if ((spanHeightPercent <= maxHeightPercentage) && (Math.abs(spanCenter - innerCenter) < 1)) {
            break;
        } else {
            if (fontSize > minFontSize + 0.5) {
                fontSize = fontSize - 0.5;
                content.style.fontSize = fontSize + "px";
            }
        }
    }
}

</script>

It's for a rhombus shape, but you can adapt it to any "shape-outside" shape with a bit of work. It's a but messy, but it works.