├── .gitattributes ├── README.md ├── index.html └── style.css /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netflix-carousel-css-only 2 | 3 | The code from [this YouTube video](https://youtu.be/b--q6Fsf_cA), where I did my best to clone the Netflix carousel UI using CSS only. 4 | 5 | **This uses the `:has()` selector**. If you want to play with the code, make sure that [you're browser supports the `:has()` selector](https://caniuse.com/css-has). 6 | 7 | Don't use anything like this in production until support is much better than when I used it (May 4th, 2022), where it was only supported in Safari and behind a flag in Chrome. 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Netflix media carousel CSS only 9 | 10 | 11 | 12 | 13 | 19 | 20 |

Trending now

21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 31 |
32 |
33 | 36 |
37 |
38 | 41 |
42 |
43 | 46 |
47 |
48 | 51 |
52 | 53 | 58 |
59 | 60 | 61 |
62 | 67 |
68 | 71 |
72 |
73 | 76 |
77 |
78 | 81 |
82 |
83 | 86 |
87 |
88 | 91 |
92 | 97 |
98 | 99 | 100 |
101 | 106 |
107 | 110 |
111 |
112 | 115 |
116 |
117 | 120 |
121 |
122 | 125 |
126 |
127 | 130 |
131 | 136 |
137 | 138 | 139 |
140 | 145 |
146 | 149 |
150 |
151 | 154 |
155 |
156 | 159 |
160 |
161 | 164 |
165 |
166 | 169 |
170 |
171 | 172 | 178 | 179 |
180 |
181 | 182 | 183 | 184 | 185 | 187 | 188 | 189 | 190 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | } 4 | 5 | *, 6 | *::before, 7 | *::after { 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | font-family: system-ui; 13 | font-size: 1.25rem; 14 | color: white; 15 | background: #121212; 16 | } 17 | 18 | img, 19 | svg { 20 | max-width: 100%; 21 | display: block; 22 | } 23 | 24 | /* general styling */ 25 | 26 | .container { 27 | inline-size: min(100% - 4rem, 70rem); 28 | margin-inline: auto; 29 | } 30 | 31 | .flow { 32 | display: grid; 33 | gap: var(--flow-spacer, 1rem); 34 | } 35 | 36 | .page-header { 37 | padding-block: 5rem; 38 | margin-block-end: 5rem; 39 | background-image: url("https://images.unsplash.com/photo-1641353989082-9b15fa661805?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0MzM5ODcyOA&ixlib=rb-1.2.1&q=80&w=1200"), 40 | linear-gradient(-25deg, rgb(0 0 0 / 0), rgb(0 0 0 / 1) 50%); 41 | background-size: cover; 42 | background-blend-mode: multiply; 43 | color: white; 44 | } 45 | 46 | .page-title { 47 | font-size: 4rem; 48 | margin: 0; 49 | } 50 | 51 | .section-title { 52 | margin-block: 4rem 1rem; 53 | } 54 | 55 | /* media scroller */ 56 | 57 | .media-container { 58 | position: relative; 59 | } 60 | 61 | .media-scroller, 62 | .media-group { 63 | display: grid; 64 | gap: 0.25rem; 65 | grid-auto-flow: column; 66 | } 67 | 68 | .media-scroller { 69 | overflow-x: hidden; 70 | scroll-behavior: smooth; 71 | grid-auto-columns: 100%; 72 | padding: 0 3rem; 73 | scroll-padding-inline: 3rem; 74 | } 75 | 76 | .media-group { 77 | grid-auto-columns: 1fr; 78 | } 79 | 80 | .media-element { 81 | border-radius: 0.25rem; 82 | overflow: hidden; 83 | } 84 | 85 | .media-element > img { 86 | width: 100%; 87 | aspect-ratio: 16 / 9; 88 | object-fit: cover; 89 | } 90 | 91 | .next, 92 | .previous { 93 | display: none; 94 | align-items: center; 95 | z-index: 10; 96 | position: absolute; 97 | width: 3rem; 98 | padding: 1rem; 99 | background: rgb(0 0 0 / 0); 100 | opacity: 0; 101 | } 102 | 103 | .previous { 104 | left: 0; 105 | top: 0; 106 | bottom: 0; 107 | } 108 | 109 | .next { 110 | right: 0; 111 | top: 0; 112 | bottom: 0; 113 | } 114 | 115 | .media-group:first-child :where(.next, .previous) { 116 | display: flex; 117 | } 118 | 119 | .media-scroller:hover :where(.next, .previous) { 120 | opacity: 1; 121 | } 122 | 123 | :where(.next, .previous):hover { 124 | background: rgb(0 0 0 / 0.3); 125 | } 126 | 127 | :where(.next, .previous) > svg { 128 | transition: transform 75ms linear; 129 | transform: scale(1); 130 | } 131 | :where(.next, .previous):hover > svg { 132 | transform: scale(1.2); 133 | } 134 | 135 | .media-group:target :where(.next, .previous) { 136 | display: flex; 137 | } 138 | 139 | .media-scroller:has(:target:not(:first-child)) 140 | .media-group:first-of-type 141 | .next { 142 | display: none; 143 | } 144 | 145 | /* navigation indicators */ 146 | 147 | .navigation-indicators { 148 | opacity: 0; 149 | position: absolute; 150 | display: flex; 151 | gap: 3px; 152 | 153 | top: -1rem; 154 | right: 2rem; 155 | } 156 | 157 | .navigation-indicators > * { 158 | width: 1rem; 159 | height: 2px; 160 | background: white; 161 | opacity: 0.5; 162 | } 163 | 164 | .media-scroller:has(.media-group:target) 165 | .navigation-indicators 166 | > *:nth-child(1) { 167 | opacity: 0.5; 168 | } 169 | 170 | .navigation-indicators > *:nth-child(1), 171 | .media-group:nth-child(1):target ~ .navigation-indicators > *:nth-child(1) { 172 | opacity: 1; 173 | } 174 | 175 | .media-group:nth-child(2):target ~ .navigation-indicators > *:nth-child(2) { 176 | opacity: 1; 177 | } 178 | 179 | .media-group:nth-child(3):target ~ .navigation-indicators > *:nth-child(3) { 180 | opacity: 1; 181 | } 182 | 183 | .media-group:nth-child(4):target ~ .navigation-indicators > *:nth-child(4) { 184 | opacity: 1; 185 | } 186 | 187 | .media-scroller:hover .navigation-indicators { 188 | opacity: 1; 189 | } 190 | --------------------------------------------------------------------------------