Rotation Santé
/* Layout: central photo fixed; 9 pills sit on a rotating ring; each pill's
text carries an equal, opposite rotation so it stays upright. */
:root{
--bg:#f4f0ea;
--panel:#ffffff;
--ink:#2a2640;
--ink-soft:#6b6680;
--accent:#c23b6b;
--glass-bg:rgba(255,255,255,0.45);
--glass-border:rgba(255,255,255,0.65);
--glass-shadow:rgba(40,30,50,0.12);
--font-display:'Poppins',system-ui,-apple-system,sans-serif;
--font-body:'Poppins',system-ui,-apple-system,sans-serif;
}
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#1c1a24; --panel:#2b2635; --ink:#f1ecf5; --ink-soft:#a79fb8; --accent:#f06a97;
--glass-bg:rgba(255,255,255,0.12); --glass-border:rgba(255,255,255,0.25); --glass-shadow:rgba(0,0,0,0.35);
color-scheme:dark;
}
}
:root[data-theme="dark"]{
--bg:#1c1a24; --panel:#2b2635; --ink:#f1ecf5; --ink-soft:#a79fb8; --accent:#f06a97;
--glass-bg:rgba(255,255,255,0.12); --glass-border:rgba(255,255,255,0.25); --glass-shadow:rgba(0,0,0,0.35);
color-scheme:dark;
}
*{box-sizing:border-box;}
body{
background:var(--bg);
color:var(--ink);
font-family:var(--font-body);
margin:0;
padding-inline:16px;
padding-block:32px;
display:flex;
flex-direction:column;
align-items:center;
gap:20px;
}
h1{
font-family:var(--font-display);
font-weight:600;
font-size:clamp(1.25rem,3vw,1.7rem);
text-align:center;
text-wrap:balance;
margin:0;
}
p.lede{
color:var(--ink-soft);
font-size:0.92rem;
text-align:center;
max-width:42ch;
margin:0;
}
.stage{
position:relative;
width:min(90vw,560px);
aspect-ratio:1/1;
}
.ring{
position:absolute;
inset:0;
animation:spin 90s linear infinite;
}
@keyframes spin{ to{ transform:rotate(360deg); } }
/* dashed tracks: two crossing ellipses behind the pills, spin with them */
.track{
position:absolute;
top:50%;
left:50%;
border-radius:50%;
border:1.5px dashed var(--accent);
opacity:0.55;
z-index:0;
}
.track--wide{ transform:translate(-50%,-50%); }
.track--tall{ transform:translate(-50%,-50%); }
/* small accent dots along the track */
.track-dot{
position:absolute;
top:50%;
left:50%;
width:7px;
height:7px;
border-radius:50%;
background:var(--accent);
box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
z-index:1;
}
/* static anchor: places a pill at its fixed angle on the circle (set by JS) */
.anchor{
position:absolute;
top:50%;
left:50%;
width:0;
height:0;
z-index:2;
}
/* counter-rotation: cancels the ring's spin so the label stays upright */
.counter{
position:absolute;
top:0;
left:0;
transform:translate(-50%,-50%) rotate(0deg);
animation:counter-spin 90s linear infinite;
animation-fill-mode:forwards;
}
@keyframes counter-spin{
from{ transform:translate(-50%,-50%) rotate(0deg); }
to{ transform:translate(-50%,-50%) rotate(-360deg); }
}
.counter span{
display:inline-block;
white-space:nowrap;
background:var(--glass-bg);
backdrop-filter:blur(10px) saturate(160%);
-webkit-backdrop-filter:blur(10px) saturate(160%);
border:1px solid var(--glass-border);
color:var(--ink);
font-size:clamp(0.66rem,1.7vw,0.8rem);
font-weight:600;
padding:0.55em 1.05em;
border-radius:999px;
box-shadow:0 4px 16px var(--glass-shadow);
}
@media (prefers-reduced-motion:reduce){
.ring, .counter{ animation:none; }
}
.center-photo{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
width:44%;
aspect-ratio:1/1;
border-radius:50%;
overflow:hidden;
border:6px solid var(--panel);
box-shadow:0 8px 28px rgba(0,0,0,0.2);
z-index:2;
}
.center-photo img{
width:100%;
height:100%;
object-fit:cover;
display:block;
}
.note{
color:var(--ink-soft);
font-size:0.78rem;
text-align:center;
max-width:56ch;
}
.note code{
background:var(--panel);
padding:0.1em 0.45em;
border-radius:6px;
font-family:ui-monospace,monospace;
}
(function(){
var labels = [
"Santé au travail",
"Relations sociales",
"Environnement de vie",
"Douleurs",
"Maladies Chroniques",
"Comportements de santé",
"Sommeil",
"Activité physique",
"Nutrition",
"Santé mentale"
];
var ring = document.getElementById('ring');
var n = labels.length;
var radiusPercent = 44; // distance of each label from center, % of stage width
var crossRatio = 0.68; // how much narrower/shorter the crossing ellipses are vs the pill circle
var dotAngles = [0, 90, 180, 270]; // accent dots on the track (compass points)
// two dashed ellipses crossing behind the pills — one wide, one tall
var trackWide = document.createElement('div');
trackWide.className = 'track track--wide';
ring.appendChild(trackWide);
var trackTall = document.createElement('div');
trackTall.className = 'track track--tall';
ring.appendChild(trackTall);
var dots = dotAngles.map(function(angleDeg){
var dot = document.createElement('div');
dot.className = 'track-dot';
ring.appendChild(dot);
return { el: dot, angleDeg: angleDeg };
});
var anchors = labels.map(function(text, i){
var angleDeg = (360 / n) * i;
var anchor = document.createElement('div');
anchor.className = 'anchor';
var counter = document.createElement('div');
counter.className = 'counter';
var span = document.createElement('span');
span.textContent = text;
counter.appendChild(span);
anchor.appendChild(counter);
ring.appendChild(anchor);
return { anchor: anchor, angleDeg: angleDeg };
});
function layout(){
var stage = document.getElementById('stage');
var size = stage.clientWidth;
var r = size * (radiusPercent / 100); // radius the pills sit on — a perfect circle
// wide ellipse: full width, reduced height
trackWide.style.width = (r * 2) + 'px';
trackWide.style.height = (r * 2 * crossRatio) + 'px';
// tall ellipse: reduced width, full height
trackTall.style.width = (r * 2 * crossRatio) + 'px';
trackTall.style.height = (r * 2) + 'px';
dots.forEach(function(item){
var rad = (item.angleDeg - 90) * Math.PI / 180;
var x = r * Math.cos(rad);
var y = r * Math.sin(rad);
item.el.style.transform =
'translate(-50%,-50%) translate(' + x.toFixed(2) + 'px, ' + y.toFixed(2) + 'px)';
});
anchors.forEach(function(item){
// Pure translation (no rotate here) so the label itself never inherits
// a placement angle — only the ring's spin reaches it, which the
// counter-rotation cancels.
var rad = (item.angleDeg - 90) * Math.PI / 180; // -90 so angle 0 = top
var x = r * Math.cos(rad);
var y = r * Math.sin(rad);
item.anchor.style.transform =
'translate(-50%,-50%) translate(' + x.toFixed(2) + 'px, ' + y.toFixed(2) + 'px)';
});
}
layout();
window.addEventListener('resize', layout);
})();