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.

Add feed

+69 -19
+5 -1
TODO.md
··· 5 5 ### Bugs 6 6 7 7 - [ ] Safari rotates the back side of the postcard the opposite direction to _every other browser_. Which is extremely annoying. I need to figure out a workaround. 8 + - [ ] Fix locale flags for Windows users 8 9 9 10 ### Ideas 10 11 11 12 - [ ] Include a map, blocking off the parts of the world that I've received a postcard from. Voronoi colouring for countries with more than one? 12 - - [ ] Add RSS feed for postcards 13 + 14 + ### Done ✔ 15 + 16 + - [x] Add RSS feed for postcards
+26
feed.xml.tmpl
··· 1 + <?xml version="1.0" encoding="utf-8" standalone="yes"?> 2 + <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"> 3 + <channel> 4 + <title>Shut up JP</title> 5 + <link>https://shutpup.jp/</link> 6 + <description>A collection of postcards sent to me by my friend, Claire, inviting me to 'shut up' from many places and in many languages over many years.</description> 7 + <language>en-GB</language> 8 + <managingEditor>hello@shutup.jp (JP Hastings-Spital)</managingEditor> 9 + <webMaster>hello@shutup.jp (JP Hastings-Spital)</webMaster> 10 + <copyright>CC BY-NC-SA 4.0</copyright> 11 + <lastBuildDate>{{ now.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</lastBuildDate> 12 + {{ range . }} 13 + <item> 14 + <title>{{ with .Location }}{{ .Name }}{{ end }}{{ with .SentOn }} ({{ .Format "January 2, 2006" }}){{ end }}</title> 15 + <link>/#{{ .Name }}</link> 16 + <pubDate>{{ with .SentOn }}{{ .Format "Mon, 02 Jan 2006 15:04:05 -0700" }}{{ end }}</pubDate> 17 + <guid>{{ .Name }}</guid> 18 + <content:encoded> 19 + <p>Another delightfully curt postcard I received from Claire on {{ with .SentOn }}{{ .Format "January 1, 2006" }}{{ end }}. It reads:</p> 20 + {{- .Back.Transcription | safeHTML -}} 21 + </content:encoded> 22 + <enclosure url="/{{.Name}}.webp" type="image/webp"/> 23 + </item> 24 + {{ end }} 25 + </channel> 26 + </rss>
+20 -7
funcs.go
··· 2 2 3 3 import ( 4 4 _ "embed" 5 - "html/template" 5 + ht "html/template" 6 + tt "text/template" 7 + "time" 6 8 ) 7 9 10 + func now() time.Time { 11 + return time.Now() 12 + } 13 + 8 14 func lastPC(pcs []Postcard) Postcard { 9 15 return pcs[len(pcs)-1] 10 16 } 11 17 12 - func safeHTML(str string) template.HTML { 13 - return template.HTML(str) 18 + func safeHTML(str string) ht.HTML { 19 + return ht.HTML(str) 20 + } 21 + 22 + var funcs = ht.FuncMap{ 23 + "safeHTML": safeHTML, 24 + "lastPC": lastPC, 25 + "now": now, 14 26 } 15 27 16 28 //go:embed index.html.tmpl 17 29 var indexStr string 18 - var indexTmpl = template.Must(template.New("index").Funcs(template.FuncMap{ 19 - "safeHTML": safeHTML, 20 - "lastPC": lastPC, 21 - }).Parse(indexStr)) 30 + var indexTmpl = ht.Must(ht.New("index").Funcs(funcs).Parse(indexStr)) 31 + 32 + //go:embed feed.xml.tmpl 33 + var feedStr string 34 + var feedTmpl = tt.Must(tt.New("feed").Funcs(funcs).Parse(feedStr))
+5 -4
index.html.tmpl
··· 11 11 <link rel="canonical" href="https://shutup.jp"> 12 12 <link rel="stylesheet" href="postcard.css"> 13 13 <link rel="stylesheet" href="shutup.css"> 14 + <link rel="alternate" type="application/rss+xml" href="/feed.xml" title="Shut up JP"> 14 15 </head> 15 16 <body> 16 17 <section class="explain"> 17 18 <p>One day, after months of silence, my friend <a href="https://www.instagram.com/claire.durrant88/">Claire</a> sent me a text message of only 3 words:</p> 18 - <h1>Shut up, JP</h1> 19 + <h1>Shut up JP</h1> 19 20 <p>Knowing my <a href="https://www.byjp.me/posts/thoughts-on-postcards/" target="_blank">love of postcards</a>, she started sending me this invaluable advice by mail too – from every country she visited, and in every language the friendly (and presumably deeply confused) locals help her write in.</p> 20 21 <p>It's just shy of a decade later and, apparently, I've <em>still</em> not got the message.</p> 21 22 <cite><a target="_blank" href="https://www.byjp.me">JP</a></cite> ··· 23 24 24 25 {{ range . }} 25 26 <figure> 26 - <input type="checkbox" id="flip-{{.Name}}"> 27 - <label for="flip-{{.Name}}"> 27 + <input type="checkbox" id="{{.Name}}"> 28 + <label for="{{.Name}}"> 28 29 <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 }}"> 29 30 <img src="{{ .Name }}.webp" loading="lazy" alt="{{ .Front.Description }}" width="500px"> 30 31 <div></div> ··· 36 37 <div class="transcription">{{- .Back.Transcription | safeHTML -}}</div> 37 38 </figcaption> 38 39 </figure> 39 - {{ end }} 40 + {{ end }} 40 41 41 42 <section class="explain">Shut up Claire.</section> 42 43 </body>
+5 -2
main.go
··· 34 34 pcs = append(pcs, pc) 35 35 toCopy = append(toCopy, "postcards/"+pc.Name+".webp") 36 36 } 37 + sort.Sort(BySentOn(pcs)) 37 38 38 39 os.Mkdir("dist/", 0755) 39 40 40 41 indexF, err := os.OpenFile(path.Join(outDir, "index.html"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) 41 42 check(err) 43 + check(indexTmpl.Execute(indexF, pcs)) 42 44 43 - sort.Sort(BySentOn(pcs)) 44 - check(indexTmpl.Execute(indexF, pcs)) 45 + feedF, err := os.OpenFile(path.Join(outDir, "feed.xml"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) 46 + check(err) 47 + check(feedTmpl.Execute(feedF, pcs)) 45 48 46 49 for _, tc := range toCopy { 47 50 src, err := os.Open(tc)
+8 -5
static/shutup.css
··· 1 1 :root { 2 2 --text-color: #222; 3 - --mute-color: #555; 3 + --mute-color: #777; 4 4 --back-color: #f7f7f7; 5 5 6 6 @media (prefers-color-scheme: dark) { 7 7 --text-color: #ddd; 8 - --mute-color: #999; 8 + --mute-color: #777; 9 9 --back-color: #222; 10 10 } 11 11 } ··· 21 21 html,body { 22 22 margin: 0; 23 23 padding: 0; 24 - font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 24 + font-family:Verdana, Geneva, Tahoma, sans-serif; 25 25 scroll-snap-type: y mandatory; 26 26 scroll-padding: 0; 27 27 ··· 30 30 } 31 31 32 32 h1 { 33 - font-size: 2rem; 33 + font-size: 2.5rem; 34 34 text-align: center; 35 35 margin: 2rem; 36 36 padding: 0; ··· 139 139 } 140 140 141 141 input { 142 - display:none; 142 + display: inline; 143 + width: 0; 144 + height: 0; 145 + overflow:hidden; 143 146 } 144 147 145 148 input:checked ~ label .postcard {