2

I've looked into this a fair bit but can't seem to find a good, solid answer to find how to make a responsive circle around a div element of variable height.

It's easy to make a simple responsive circle using vw units.

<div style="height:20vw; width:20vw"></div>

However, I'm looking to use a min-height of an element and have a circle around this div.

Another way to create a responsive circle is using something like the snippet below, but again I can't adapt this to work for a variable height (again, I can't use vh units as the div will change in height.

.square {
  position: relative;
  width: 10%;
  background: gray;
  border-radius: 50%;
}

.square:after {
  content: "";
  display: block;
  padding-bottom: 100%;
}

.content {
  position: absolute;
  width: 100%;
  height: 100%;
}
<div class="square">
  <div class="content">

  </div>
</div>

I am trying to create something like the below, where the circle will never cut into the corners of the div (with around a 10px padding). I personally was trying to avoid javascript and would have preferred a css only approach, but it seems it's unavoidable. Maybe the only solution is to use a jquery to calculate the height of the element in order to apply this to a wrapper element?

enter image description here

I was playing around with this:

.square {
  position: absolute;
  top: 50%;
  display: inline-block;
  left: 50%;
  transform: translate(-50%, -50%);
  min-height: 100px;
  border-radius: 50%;
  background: url('https://i.imgur.com/2dxaFs9_d.webp?maxwidth=640&shape=thumb&fidelity=medium');
  background-size: 100% 100%;
  padding: 20px;
}

.content {
  width: 300px;
  min-height: 100px;
  background: tomato;
}
<div class="square">
  <div class="content">
    Hello!<br>
    <br><br><br>This has a variable height but fixed width<br><br><br>Hello
  </div>
</div>
jbutler483
  • 24,074
  • 9
  • 92
  • 145
  • 1
    Related https://stackoverflow.com/questions/41855730/css-circle-without-using-fixed-width-and-height – Paulie_D Jul 19 '20 at 18:27
  • Hey @Paulie_D I saw that one but doesn't like the multi-line aspect. Closest I've got to is [this](https://jsfiddle.net/8ra3wscx/3/). But still doesn't want to have a gap around the child element and the parent. – jbutler483 Jul 19 '20 at 18:35
  • But I thought you want a solution without javascript ? If you accept javascript this is really not hard to achieve because you can calculate the diagonale of your content box divide by 2, add the padding you want, and this is the radius of your circle. Apply it in both size. – iguypouf Jul 19 '20 at 19:52
  • I would prefer to use CSS only but it seems js is the only option here (despite the power of CSS functionality) – jbutler483 Jul 19 '20 at 19:55

4 Answers4

6

Clip-path can easily do this if you consider solid coloration.

Resize the element and the circle will follow:

.box {
  width: 200px;
  height: 200px;
  overflow: hidden;
  resize: both;
  background: blue;
  box-shadow: 0 0 0 200vmax red;
  clip-path: circle(71%);
  margin: 100px auto;
}
<div class="box"></div>

Related question to understand the magic number 71%: clip-path:circle() radius doesn't seem to be calculated correctly


To use an image we can consider pseudo elements. You can also rely on calc() to add the offset:

.box {
  width: 200px;=
  resize: both;
  clip-path: circle(calc(71% + 10px));
  margin: 100px auto;
  position: relative;
  font-size:35px;
  color:#fff;
}
/* the background layer */
.box::before {
  content:"";
  position: absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background:blue;
}

/* the image layer */
.box::after {
  content:"";
  position: fixed; /* to make sure the image cover all the screen */
  z-index:-2;
  top:0;
  bottom:0;
  left:0;
  right:0;
  background:url(https://picsum.photos/id/1015/1000/1000) center/cover no-repeat;
}
<div class="box" contenteditable="true"> Edit this<br>text </div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
2

I tried my hardest to figure this out with pure css. Though the problem with css I could not figure out how to calculate the diameter of the circle based on the content div size; the length from top left corner to bottom right corner of the variable height div.

I'm not sure if can be done using the calc() css function.

But I did manage to do it with a little jquery (which could easily be changed to pure javascript if you are not using jquery).

See working resizable example below (follow my comments in code)

Note: If you are using internet explorer the resizable demo content div will not resize.

// circumscriber for variable size divs
function circumscriber() {

  // for each variable size div on page
  $(".variable-size").each(function() {

    // get the variable size div content width and height
    let width = $(this).outerWidth();
    let height = $(this).outerHeight();

    // get the diameter for our pefect circle based on content size
    let diameter = Math.sqrt(width ** 2 + height ** 2);

    // extra 15 pixel circle edge around variable size div
    let edge = 15;
    
    // add current circle size width css
    $('.circle', this).css({
      'width': (diameter + (edge * 2)) + 'px'
    })

  });

}

// run the circumscriber (you might wana do this on ready)
circumscriber();

// if the window is resized responsively
$(window).on('resize', function() {
  circumscriber();
});


// for demo purpose to fire circumscriber when resizing content
// not needed for real thing
$('.content').on('input', function() {
  this.style.height = "";
  this.style.height = ( this.scrollHeight - 30 ) + "px";
  circumscriber();
}).on('mouseup', function() {
  circumscriber();
});
/* variable size container to be circumscribed by circle */
/* none of these styles are required, this just to center the variable size div in the window for demo purposes */
.variable-size {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* resizable text area for demo */
/* again not needed */
.variable-size .content {
  padding: 15px;
  background: #fff;
  resize: both;
  overflow: auto;
  color: #000;
  border: none;
  width: 200px;
  font-weight: bold;
}

.variable-size .content:focus {
  outline: 0;
}

/* child circle div css */
.variable-size .circle {
  position: absolute;
  background-image: url('https://i.imgur.com/2dxaFs9_d.webp?maxwidth=640&shape=thumb&fidelity=medium');
  background-position: center center;
  z-index: -1;
  border-radius: 50%;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  transition: all 0.5s ease;
  width: 0;
}

/* fast way to make circle height the same as current width */
.variable-size .circle:before {
  display: block;
  content: '';
  width: 100%;
  padding-top: 100%;
}

/* demo window css */
HTML,
BODY {
  height: 100%;
  min-height: 100%;
  background: black;
  position: relative;
  font-family: "Lucida Console", Courier, monospace;
}
<div class="variable-size">
  <textarea class="content" rows="1" placeholder="TYPE TEXT OR RESIZE ME &#8600;"></textarea>
  <div class="circle"></div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

See jsfiddle here... https://jsfiddle.net/joshmoto/6d0zs7uq/

joshmoto
  • 4,472
  • 1
  • 26
  • 45
-3
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.stroke();

Source: https://www.w3schools.com/

Ashish
  • 647
  • 7
  • 18
-3

You could use flex display and insert empty flex-items around the inner div and use flex-basis to fix their width.

Try this

.square {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  min-height: 100px;
  border-radius: 50%;
  background: black;
  background-size: 100% 100%;
  padding: 20px;
}

.content {
  width: 300px;
  min-height: 100px;
  background: tomato;
}

.emptyDiv {
  flex-basis: 120px
}
<div class="square">
  <div class="emptyDiv"></div>
  <div class="content">
    Hello!<br>
    <br><br><br>This has a variable height but fixed width<br><br><br>Hello
  </div>
  <div class="emptyDiv"></div>
</div>
Sean
  • 6,873
  • 4
  • 21
  • 46
alchemist95
  • 759
  • 2
  • 9
  • 26