<div class="demo-container">
<h2>Calculating circle placement</h2>
<p>Using <code>sibling-index()</code> and <code>sibling-count()</code> to pass through some trigonometric functions using <code>@function</code></p>
<section class="controls">
<button id="add-item">Add Item</button>
<button id="remove-item">Remove Item</button>
</section>
<section class="circle-wrapper">
<div class="circle-container">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</section>
</div>
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap");
@function --pos-x(--index, --count, --radius) {
result: calc(var(--radius) * cos(360deg / var(--count) * (var(--index) - 1)));
}
@function --pos-y(--index, --count, --radius) {
result: calc(var(--radius) * sin(360deg / var(--count) * (var(--index) - 1)));
}
:root {
--item-size: 60px;
--stage-size: calc(
var(--item-size) * 6
); /* This is quite a static calculation for the maximum of 12 items, children-count() would be nice */
/* Presentational vars */
--bg-color: #f0f2f5;
--text-color: #333;
--items-bg: conic-gradient(
at 0% 1% in oklch,
oklch(62% 0.25 348) 0%,
oklch(73% 0.28 241) 100%
);
}
.circle-wrapper {
width: var(--stage-size);
height: var(--stage-size);
display: grid;
margin-inline: auto;
place-content: center;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
}
/* The circle container is the size of one item, the items get place around it with transforms. it is the center point */
.circle-container {
position: relative;
width: var(--item-size);
height: var(--item-size);
div {
--radius: calc(var(--item-size) * 2); /* The radius of the circle */
position: absolute;
top: 50%;
left: 50%;
/* Using functions in order to have a cleaner value output here
First a general center translate and then overrule, seems to work with the matrix :o
*/
transform: translate(-50%, -50%)
translateX(--pos-x(sibling-index(), sibling-count(), var(--radius)))
translateY(--pos-y(sibling-index(), sibling-count(), var(--radius)));
width: var(--item-size);
height: var(--item-size);
border-radius: 50%;
background: var(--items-bg);
display: grid;
place-content: center;
font-size: 1.5rem;
font-weight: 600;
color: white;
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
}
@layer basestyles {
:root {
--bg-color: #f0f2f5;
--text-color: #333;
--accent-grad: linear-gradient(160deg, hotpink, indigo);
}
body {
font-family: "Poppins", sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
padding: 3rem 1rem;
display: grid;
gap: 3.5rem;
justify-items: center;
}
.demo-container {
width: 100%;
max-width: 650px;
text-align: center;
}
h2 {
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1.5rem;
color: #666;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
code {
background-color: #e2e5e9;
padding: 0.2em 0.4em;
border-radius: 4px;
font-size: 0.9em;
}
button {
font-family: inherit;
font-size: 1rem;
font-weight: 600;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
background-image: var(--accent-grad);
color: white;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
margin: 0 0.5rem 1.5rem;
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px B20px rgba(138, 126, 255, 0.4);
}
}
}
const circleContainer = document.querySelector(".circle-container");
const addItemBtn = document.getElementById("add-item");
const removeItemBtn = document.getElementById("remove-item");
const MAX_COUNT = 12;
addItemBtn.addEventListener("click", () => {
if (circleContainer.children.length < MAX_COUNT) {
const newItem = document.createElement("div");
newItem.textContent = circleContainer.children.length + 1;
circleContainer.appendChild(newItem);
}
});
removeItemBtn.addEventListener("click", () => {
if (circleContainer.lastElementChild) {
circleContainer.removeChild(circleContainer.lastElementChild);
}
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.