I get told to shut up a lot by my friend. This is the microsite that documents this in detail. shutup.jp
postcards microsite
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Full page, clickable (broken for portrait)

This is a less resource intensive design, using click interactions to flip (for javascript) or hover (for just CSS)

+65 -80
+2
.gitignore
··· 1 1 /dist/ 2 2 .env 3 + # Temporary, while I'm working 4 + /postcards/
+8 -29
index.html.tmpl
··· 21 21 {{ range . }} 22 22 <figure> 23 23 <div class="postcard {{ .Flip }} {{ if gt .FrontDimensions.PxHeight .FrontDimensions.PxWidth }}portrait{{ else }}landscape{{ end }}" style="--postcard: url('{{ .Name }}.webp'); --aspect-ratio: {{ .FrontDimensions.PxWidth }} / {{ .FrontDimensions.PxHeight }}"> 24 - <img src="{{ .Name }}.webp" alt="{{ .Front.Description }}"> 24 + <img src="{{ .Name }}.webp" loading="lazy" alt="{{ .Front.Description }}" width="500px"> 25 + <div></div> 25 26 </div> 26 27 <!--<figcaption style="--map: url(seattle.svg)">--> 27 28 <figcaption> ··· 34 35 <section class="explain">How fun is this??</section> 35 36 36 37 <script> 37 - const debounce = (fn) => { 38 - let frame; 39 - return (...params) => { 40 - if (frame) { 41 - cancelAnimationFrame(frame); 42 - } 43 - frame = requestAnimationFrame(() => { 44 - fn(...params); 45 - }); 46 - } 47 - }; 48 - 49 - const storeScroll = () => { 50 - document.documentElement.style.setProperty('--scroll', window.scrollY); 51 - } 52 - document.addEventListener('scroll', storeScroll, { passive: true }); 53 - storeScroll(); 54 - 55 - const setYPos = () => { 56 - document.documentElement.style.setProperty('--viewportY', Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)); 57 - Array.from(document.getElementsByClassName('postcard')).forEach((pc) => { 58 - pc.style.setProperty('--yPos', 59 - window.scrollY + pc.getBoundingClientRect().top + pc.getBoundingClientRect().height / 2 60 - ); 61 - }) 62 - } 63 - window.addEventListener('resize', setYPos); 64 - setYPos(); 38 + window.addEventListener('DOMContentLoaded', () => { 39 + for (const pc of document.getElementsByClassName('postcard')) { 40 + pc.classList.add('clickable'); 41 + pc.addEventListener('click', () => pc.classList.toggle('flipped')); 42 + } 43 + }); 65 44 </script> 66 45 </body> 67 46 </html>
+10 -16
static/postcard.css
··· 6 6 flex-direction: row; 7 7 align-items: center; 8 8 9 + --p: 1000px; 9 10 --v1: calc(min(1, max(0, 10 11 cos(var(--flip)), 11 12 cos(var(--flip) + 180deg), ··· 54 55 mask: var(--postcard) bottom/100%; 55 56 } 56 57 57 - .postcard * { 58 + .postcard img { 58 59 display:none; 59 60 } 60 61 ··· 63 64 } 64 65 65 66 .postcard.book::after { 66 - transform: perspective(800px) rotateY(var(--flip)); 67 + transform: perspective(var(--p)) rotateY(var(--flip)); 67 68 background-image: linear-gradient(to right, 68 69 rgb(var(--v1), var(--v1), var(--v1)), 69 70 rgb(var(--v2), var(--v2), var(--v2)) 70 71 ), var(--postcard); 71 72 } 72 73 .postcard.book::before { 73 - transform: perspective(800px) rotateY(calc(var(--flip) + 180deg)); 74 + transform: perspective(var(--p)) rotateY(calc(var(--flip) + 180deg)); 74 75 background-image: linear-gradient(to right, 75 76 rgb(var(--v1), var(--v1), var(--v1)), 76 77 rgb(var(--v2), var(--v2), var(--v2)) ··· 78 79 } 79 80 80 81 .postcard.calendar::after { 81 - transform: perspective(800px) rotateX(var(--flip)); 82 + transform: perspective(var(--p)) rotateX(var(--flip)); 82 83 background-image: linear-gradient(to top, 83 84 rgb(var(--v1), var(--v1), var(--v1)), 84 85 rgb(var(--v2), var(--v2), var(--v2)) 85 86 ), var(--postcard); 86 87 } 87 88 .postcard.calendar::before { 88 - transform: perspective(800px) rotateX(calc(var(--flip) + 180deg)); 89 + transform: perspective(var(--p)) rotateX(calc(var(--flip) + 180deg)); 89 90 background-image: linear-gradient(to top, 90 91 rgb(var(--v1), var(--v1), var(--v1)), 91 92 rgb(var(--v2), var(--v2), var(--v2)) ··· 93 94 } 94 95 95 96 .postcard.right-hand::after { 96 - transform: perspective(800px) rotate3d(1,1,0,var(--flip)); 97 + transform: perspective(var(--p)) rotate3d(1,1,0,var(--flip)); 97 98 background-image: linear-gradient(to top right, 98 99 rgb(var(--v1), var(--v1), var(--v1)), 99 100 rgb(var(--v2), var(--v2), var(--v2)) 100 101 ), var(--postcard); 101 102 } 102 103 .postcard.right-hand::before { 103 - transform: perspective(800px) rotate3d(1,1,0,calc(var(--flip) + 180deg)) rotate(-90deg); 104 + transform: perspective(var(--p)) rotate3d(1,1,0,calc(var(--flip) + 180deg)) rotate(-90deg); 104 105 background-image: linear-gradient(to bottom right, 105 106 rgb(var(--v1), var(--v1), var(--v1)), 106 107 rgb(var(--v2), var(--v2), var(--v2)) ··· 108 109 } 109 110 110 111 .postcard.left-hand::after { 111 - transform: perspective(800px) rotate3d(-1,1,0,var(--flip)); 112 + transform: perspective(var(--p)) rotate3d(-1,1,0,var(--flip)); 112 113 background-image: linear-gradient(to bottom right, 113 114 rgb(var(--v1), var(--v1), var(--v1)), 114 115 rgb(var(--v2), var(--v2), var(--v2)) 115 116 ), var(--postcard); 116 117 } 117 118 .postcard.left-hand::before { 118 - transform: perspective(800px) rotate3d(-1,1,0,calc(var(--flip) + 180deg)) rotate(90deg); 119 + transform: perspective(var(--p)) rotate3d(-1,1,0,calc(var(--flip) + 180deg)) rotate(90deg); 119 120 background-image: linear-gradient(to top right, 120 121 rgb(var(--v1), var(--v1), var(--v1)), 121 122 rgb(var(--v2), var(--v2), var(--v2)) 122 123 ), var(--postcard); 123 124 } 124 - 125 - /* Hacks */ 126 - 127 - /* Webkit can't handle masks without stuttering, so disable them; postcards will have a white border */ 128 - _::-webkit-full-page-media, _:future, :root .postcard::before, :root .postcard::after { 129 - mask: none; 130 - }
+45 -35
static/shutup.css
··· 1 1 :root { 2 - --front: #222; 3 - --muted: #555; 4 - --back: #f7f7f7; 2 + --text-color: #222; 3 + --mute-color: #555; 4 + --back-color: #f7f7f7; 5 5 6 6 @media (prefers-color-scheme: dark) { 7 - --front: #ddd; 8 - --muted: #999; 9 - --back: #222; 7 + --text-color: #ddd; 8 + --mute-color: #999; 9 + --back-color: #222; 10 10 } 11 - 12 - --row-padding: 8; 13 - --row-height: calc(50 - var(--row-padding)); 14 11 } 15 12 16 13 a { 17 - color: var(--muted); 14 + color: var(--mute-color); 18 15 } 19 16 20 17 a:hover { 21 - color: var(--front); 18 + color: var(--text-color); 22 19 } 23 20 24 21 html,body { ··· 26 23 padding: 0; 27 24 font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 28 25 scroll-snap-type: y mandatory; 29 - scroll-padding: calc(var(--row-padding) * 1vh); 26 + scroll-padding: 0; 30 27 31 - background-color: var(--back); 32 - color: var(--front); 28 + background-color: var(--back-color); 29 + color: var(--text-color); 33 30 } 34 31 35 32 h1 { ··· 38 35 margin: 2rem; 39 36 padding: 0; 40 37 } 41 - p { 42 - min-width: 350px; 43 - width: 45vw; 44 - } 45 38 section, figure { 46 - height: calc(var(--row-height) * 1vh); 39 + height: 100vh; 40 + padding: 1em; 41 + margin: 0; 47 42 display: flex; 48 43 align-items: center; 49 44 justify-content: center; 50 - margin: 0; 51 45 scroll-snap-align: start; 52 - padding: 0 10vh; 46 + flex-direction: row; 53 47 } 48 + 49 + @media (orientation: portrait) { 50 + figure { 51 + flex-direction: column; 52 + } 53 + 54 + figcaption { 55 + min-height: 25vh; 56 + } 57 + } 58 + 54 59 figcaption { 60 + min-width: 25vw; 55 61 position: relative; 56 - height: 50%; 57 - width: 40%; 58 - margin: 5%; 59 - background-image: var(--map); 60 - background-size: contain; 61 - background-position: center; 62 - background-repeat: no-repeat; 63 62 white-space: pre; 64 63 } 65 64 66 65 section.explain { 67 - height: 50vh; 66 + display: grid; 67 + place-content: center; 68 68 flex-direction: column; 69 + padding-inline: calc(max((100vw - 500px)/2, 2em)); 69 70 } 71 + 70 72 p { 71 73 text-align:justify; 72 74 } 75 + 73 76 .postcard { 74 77 height: 90%; 78 + max-width: 90%; 75 79 aspect-ratio:1/1; 76 - --flip: calc( 77 - ( 78 - (var(--scroll) - var(--yPos)) * 50/(var(--viewportY) * var(--row-height)) 79 - + (var(--row-padding) / (2 * var(--row-height))) 80 - + 0.75 81 - ) * 360deg); 80 + --flip: 0deg; 81 + } 82 + .postcard.clickable { 83 + cursor: pointer; 84 + } 85 + 86 + .postcard.clickable.flipped,.postcard:not(.clickable):hover { 87 + --flip: 180deg; 88 + } 89 + 90 + .postcard::before,.postcard::after { 91 + transition: transform 1s ease-in-out; 82 92 } 83 93 84 94 svg polygon {