← back to Only CSS: Joint Animationcompare with ground truth →
model output
GPT-5.4
Only CSS: Joint Animation
A 0.82T 0.50
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8" />
5<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6<title>Pivoting Bars Animation</title>
7<style>
8 html, body {
9 margin: 0;
10 width: 100%;
11 height: 100%;
12 overflow: hidden;
13 background: #fff;
14 font-family: sans-serif;
15 }
16
17 .stage {
18 position: relative;
19 width: 100vw;
20 height: 100vh;
21 background: #ffffff;
22 }
23
24 /* overall placement roughly centered like the frames */
25 .rig {
26 position: absolute;
27 left: 50%;
28 top: 50%;
29 width: 420px;
30 height: 260px;
31 transform: translate(-50%, -50%);
32 }
33
34 .bar {
35 position: absolute;
36 width: 300px;
37 height: 70px;
38 transform-origin: 50px 35px;
39 opacity: .82;
40 }
41
42 .bar::before {
43 content: "";
44 position: absolute;
45 width: 14px;
46 height: 14px;
47 left: 33px;
48 top: 28px;
49 border-radius: 50%;
50 background: #ffd400;
51 box-shadow: 0 0 0 4px rgba(255,255,255,.95);
52 }
53
54 .gray {
55 background: #8f8f8f;
56 animation: grayMove 2.8s ease-in-out infinite;
57 z-index: 2;
58 }
59
60 .blue {
61 background: #3ea0d4;
62 animation: blueMove 2.8s ease-in-out infinite;
63 z-index: 1;
64 }
65
66 /* faint dashed "ghost" rectangles visible in intermediate frames */
67 .ghost {
68 position: absolute;
69 width: 300px;
70 height: 70px;
71 border: 1.5px dashed rgba(0,0,0,.28);
72 box-sizing: border-box;
73 pointer-events: none;
74 opacity: 0;
75 }
76
77 .ghost.grayGhost {
78 transform-origin: 50px 35px;
79 animation: grayGhost 2.8s ease-in-out infinite;
80 z-index: 0;
81 }
82
83 .ghost.blueGhost {
84 transform-origin: 50px 35px;
85 animation: blueGhost 2.8s ease-in-out infinite;
86 z-index: 0;
87 }
88
89 /*
90 Motion approximates the sequence:
91 - start horizontal overlap
92 - gray rotates up around its left pivot while blue slides/settles below
93 - both pass near vertical
94 - continue to opposite diagonal
95 - return
96 */
97 @keyframes grayMove {
98 0% {
99 transform: translate(40px, 40px) rotate(0deg);
100 }
101 18% {
102 transform: translate(34px, 52px) rotate(0deg);
103 }
104 38% {
105 transform: translate(30px, 58px) rotate(22deg);
106 }
107 52% {
108 transform: translate(28px, 62px) rotate(72deg);
109 }
110 68% {
111 transform: translate(18px, 72px) rotate(82deg);
112 }
113 84% {
114 transform: translate(26px, 60px) rotate(28deg);
115 }
116 100% {
117 transform: translate(40px, 40px) rotate(0deg);
118 }
119 }
120
121 @keyframes blueMove {
122 0% {
123 transform: translate(265px, 45px) rotate(0deg);
124 }
125 18% {
126 transform: translate(245px, 95px) rotate(0deg);
127 }
128 38% {
129 transform: translate(230px, 160px) rotate(0deg);
130 }
131 52% {
132 transform: translate(210px, 175px) rotate(0deg);
133 }
134 68% {
135 transform: translate(175px, 182px) rotate(0deg);
136 }
137 84% {
138 transform: translate(215px, 150px) rotate(0deg);
139 }
140 100% {
141 transform: translate(265px, 45px) rotate(0deg);
142 }
143 }
144
145 @keyframes grayGhost {
146 0%, 20%, 100% {
147 opacity: 0;
148 transform: translate(40px, 40px) rotate(0deg);
149 }
150 30% {
151 opacity: .9;
152 transform: translate(34px, 52px) rotate(22deg);
153 }
154 48% {
155 opacity: .9;
156 transform: translate(28px, 62px) rotate(72deg);
157 }
158 62% {
159 opacity: .9;
160 transform: translate(18px, 72px) rotate(82deg);
161 }
162 80% {
163 opacity: .9;
164 transform: translate(26px, 60px) rotate(28deg);
165 }
166 }
167
168 @keyframes blueGhost {
169 0%, 20%, 100% {
170 opacity: 0;
171 transform: translate(265px, 45px) rotate(0deg);
172 }
173 30% {
174 opacity: .9;
175 transform: translate(245px, 95px) rotate(-22deg);
176 }
177 48% {
178 opacity: .9;
179 transform: translate(210px, 175px) rotate(-78deg);
180 }
181 62% {
182 opacity: .9;
183 transform: translate(175px, 182px) rotate(-82deg);
184 }
185 80% {
186 opacity: .9;
187 transform: translate(215px, 150px) rotate(-28deg);
188 }
189 }
190</style>
191</head>
192<body>
193 <div class="stage">
194 <div class="rig">
195 <div class="ghost grayGhost"></div>
196 <div class="ghost blueGhost"></div>
197 <div class="bar blue"></div>
198 <div class="bar gray"></div>
199 </div>
200 </div>
201</body>
202</html>