1<!doctype html> 2<html lang="en" itemscope itemtype="https://schema.org/Article"> 3<head> 4 <meta charset="utf-8"> 5 <title>CSS-Only 3D Sphere - Leandro Linares</title> 6 <meta name="viewport" content="width=device-width"> 7 <meta name="theme-color" content="#d9514e"> 8 <meta name="description" content="21 stacked, bordered elements separated by equal distances create the perception of volume."> 9 <link rel="icon" href="/favicon.gif"> 10 <link rel="apple-touch-icon" href="/apple-touch-icon.png"> 11 <link rel="manifest" href="/manifest.json"> 12 <link rel="alternate" href="/feed.xml" type="application/rss+xml" title="Leandro Linares"> 13 <link rel="stylesheet" href="/styles.css"> 14 <link rel="canonical" href="https://leandrolinares.com/dev/css-sphere/"> 15 16 <meta itemprop="mainEntityOfPage" content="https://leandrolinares.com/dev/css-sphere/"> 17 <meta itemprop="inLanguage" content="en"> 18 <meta itemprop="image" content="https://leandrolinares.com/thumbnails/css-sphere.png"> 19 <meta name="twitter:site" content="@lean8086"> 20 <meta name="twitter:url" content="https://leandrolinares.com/dev/css-sphere/"> 21 <meta name="twitter:title" content="CSS-Only 3D Sphere"> 22 <meta name="twitter:description" content="21 stacked, bordered elements separated by equal distances create the perception of volume."> 23 <meta name="twitter:creator" content="@lean8086"> 24 <meta name="twitter:card" content="summary_large_image"> 25 <meta name="twitter:image" content="https://leandrolinares.com/thumbnails/css-sphere.png"> 26 <meta property="og:title" content="CSS-Only 3D Sphere"> 27 <meta property="og:type" content="article"> 28 <meta property="og:url" content="https://leandrolinares.com/dev/css-sphere/"> 29 <meta property="og:description" content="21 stacked, bordered elements separated by equal distances create the perception of volume."> 30 <meta property="og:site_name" content="Leandro Linares"> 31 <meta property="og:image" content="https://leandrolinares.com/thumbnails/css-sphere.png"> 32 33 <style> 34 :root { 35 --diameter: 50vmin; 36 --color-dark: #2a2b2d; 37 --color-light: #d9514e; 38 --gutter: 5%; 39 } 40 41 /* LAYOUT */ 42 43 html { 44 height: 100%; 45 } 46 47 body { 48 background-color: var(--color-light); 49 display: flex; 50 flex-direction: column; 51 } 52 53 .experiment-area { 54 flex: auto; 55 } 56 57 header a { 58 color: var(--color-dark); 59 } 60 61 .experiment-area ol, .ring { 62 width: var(--diameter); 63 height: var(--diameter); 64 list-style: none; 65 box-sizing: border-box; 66 transform-style: preserve-3d; 67 pointer-events: none; 68 } 69 70 .experiment-area ol { 71 transform: translateX(-25%) translateY(-100%) rotateX(67deg) rotateY(30deg) rotateZ(30deg); 72 padding-left: 0; 73 animation: tease 7s linear infinite; 74 position: absolute; 75 left: 50%; 76 top: 50%; 77 } 78 79 /* IMPLEMENTATION STARTS HERE */ 80 81 .ring { 82 transform: rotateX(-90deg); 83 margin-bottom: calc(-1 * var(--diameter) + var(--gutter)); 84 display: flex; 85 align-items: center; 86 justify-content: center; 87 transform-origin: top; 88 } 89 90 .ring::after { 91 content: ''; 92 display: block; 93 border: 2px solid var(--color-dark); 94 width: var(--diameter); 95 height: var(--diameter); 96 border-radius: 100%; 97 animation: spin 1s linear infinite; 98 } 99 100 .ring:nth-child(1)::after, .ring:nth-last-child(1)::after { width: 21%; height: 21%; } 101 .ring:nth-child(2)::after, .ring:nth-last-child(2)::after { width: 42%; height: 42%; } 102 .ring:nth-child(3)::after, .ring:nth-last-child(3)::after { width: 59%; height: 59%; } 103 .ring:nth-child(4)::after, .ring:nth-last-child(4)::after { width: 70%; height: 70%; } 104 .ring:nth-child(5)::after, .ring:nth-last-child(5)::after { width: 79%; height: 79%; } 105 .ring:nth-child(6)::after, .ring:nth-last-child(6)::after { width: 86%; height: 86%; } 106 .ring:nth-child(7)::after, .ring:nth-last-child(7)::after { width: 91%; height: 91%; } 107 .ring:nth-child(8)::after, .ring:nth-last-child(8)::after { width: 95%; height: 95%; } 108 .ring:nth-child(9)::after, .ring:nth-last-child(9)::after { width: 97%; height: 97%; } 109 .ring:nth-child(10)::after, .ring:nth-last-child(10)::after { width: 99%; height: 99%; } 110 111 .ring:nth-child(1)::after { animation-delay: 100ms; } 112 .ring:nth-child(2)::after { animation-delay: 200ms; } 113 .ring:nth-child(3)::after { animation-delay: 300ms; } 114 .ring:nth-child(4)::after { animation-delay: 400ms; } 115 .ring:nth-child(5)::after { animation-delay: 500ms; } 116 .ring:nth-child(6)::after { animation-delay: 600ms; } 117 .ring:nth-child(7)::after { animation-delay: 700ms; } 118 .ring:nth-child(8)::after { animation-delay: 800ms; } 119 .ring:nth-child(9)::after { animation-delay: 900ms; } 120 .ring:nth-child(10)::after { animation-delay: 1000ms; } 121 .ring:nth-child(11)::after { animation-delay: 1100ms; } 122 .ring:nth-child(12)::after { animation-delay: 1200ms; } 123 .ring:nth-child(13)::after { animation-delay: 1300ms; } 124 .ring:nth-child(14)::after { animation-delay: 1400ms; } 125 .ring:nth-child(15)::after { animation-delay: 1500ms; } 126 .ring:nth-child(16)::after { animation-delay: 1600ms; } 127 .ring:nth-child(17)::after { animation-delay: 1700ms; } 128 .ring:nth-child(18)::after { animation-delay: 1800ms; } 129 .ring:nth-child(19)::after { animation-delay: 1900ms; } 130 .ring:nth-child(20)::after { animation-delay: 2000ms; } 131 .ring:nth-child(21)::after { animation-delay: 2100ms; } 132
133 @keyframes tease { 134 from { transform: translateX(-25%) translateY(-100%) rotateX(67deg) rotateY(30deg) rotateZ(0deg); } 135 to { transform: translateX(-25%) translateY(-100%) rotateX(67deg) rotateY(30deg) rotateZ(360deg); } 136 } 137 138 @keyframes spin { 139 from { transform: rotateZ(0deg); } 140 to { transform: rotateZ(360deg); } 141 } 142 143 /* VARIATIONS */ 144 145 [data-len='3'] .ring::after { 146 border-bottom-color: transparent; 147 } 148 149 [data-len='2'] .ring::after { 150 border-bottom-color: transparent; 151 border-left-color: transparent; 152 } 153 154 [data-len='1'] .ring::after { 155 border-bottom-color: transparent; 156 border-left-color: transparent; 157 border-top-color: transparent; 158 } 159 160 [name='len'] { 161 -webkit-appearance: none; 162 position: absolute; 163 left: 50%; 164 top: 85%; 165 transform: translateX(-50%); 166 } 167 168 [name='len']::-webkit-slider-runnable-track { 169 background: var(--color-dark); 170 height: 2px; 171 } 172 173 [name='len']::-webkit-slider-thumb { 174 margin-top: -6px; 175 width: 14px; 176 height: 14px; 177 background: var(--color-dark); 178 border-radius: 100%; 179 -webkit-appearance: none; 180 cursor: pointer; 181 } 182 </style> 183</head> 184 185<body> 186 <header class="header" role="banner"> 187 <a class="header__logo pixelated" href="/">Leandro's Web Site</a> 188 <!-- <a href="https://github.com/lean8086/blog/blob/gh-pages/dev/css-sphere/index.html" target="_blank">View Source Code</a> --> 189 </header> 190 191 <main class="experiment-area"> 192 <ol> 193 <li class="ring"></li> 194 <li class="ring"></li> 195 <li class="ring"></li> 196 <li class="ring"></li> 197 <li class="ring"></li> 198 <li class="ring"></li> 199 <li class="ring"></li> 200 <li class="ring"></li> 201 <li class="ring"></li> 202 <li class="ring"></li> 203 <li class="ring"></li> 204 <li class="ring"></li> 205 <li class="ring"></li> 206 <li class="ring"></li> 207 <li class="ring"></li> 208 <li class="ring"></li> 209 <li class="ring"></li> 210 <li class="ring"></li> 211 <li class="ring"></li> 212 <li class="ring"></li> 213 <li class="ring"></li> 214 </ol> 215 <input type="range" value="4" min="1" max="4" name="len"> 216 </main> 217 218 <!-- <footer class="footer" role="contentinfo"><a href="/">Leandro Linares</a></footer> --> 219 220
220<script> 221 const list = document.querySelector('ol'); 222 document.querySelector('[name=len]').addEventListener('input', (ev) => { list.dataset.len = ev.target.value }); 223 </script>
223 224</body> 225</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.