Compare commits

..

3 Commits
🎨 ... jxl

Author SHA1 Message Date
rhiannon morris 84f822f73b <picture> elements for jxls 2023-01-17 18:45:12 +01:00
rhiannon morris 0f20753808 generate jxl from png,jpg,gif 2023-01-15 22:40:00 +01:00
rhiannon morris 76ccbe3c7d styles for chatlogs etc 2023-01-15 21:38:43 +01:00
15 changed files with 257 additions and 199 deletions

View File

@ -66,14 +66,9 @@ $(TMPDIR)/%_big.jpg: $(DATADIR)/%.jpg
$(call resize,$(BIG),$(BIG),>) $(call resize,$(BIG),$(BIG),>)
$(TMPDIR)/%_small.webp: $(DATADIR)/%.webp %.jxl: %.png ; $(call cjxl,-e 9)
$(call resize,$(SMALL),$(SMALL),^,-gravity center -crop 1:1+0) %.jxl: %.jpg ; $(call cjxl,-e 9)
%.jxl: %.gif ; $(call cjxl)
$(TMPDIR)/%_med.webp: $(DATADIR)/%.webp
$(call resize,$(MEDW),$(MEDH),>)
$(TMPDIR)/%_big.webp: $(DATADIR)/%.webp
$(call resize,$(BIG),$(BIG),>)
$(MAKEPAGES): make-pages/*.hs make-pages/make-pages.cabal $(MAKEPAGES): make-pages/*.hs make-pages/make-pages.cabal
@ -143,6 +138,14 @@ mkdir -p "$(dir $@)"
convert -resize "$(1)x$(2)$(3)" $(4) "$^" "$@" convert -resize "$(1)x$(2)$(3)" $(4) "$^" "$@"
endef endef
# args:
# 1. extra flags
define cjxl
echo "[cjxl] "$@
mkdir -p "$(dir $@)"
cjxl -d 1 "$^" "$@" $(1) 2>/dev/null
endef
# no args # no args
define depend-gallery define depend-gallery
echo "[deps] "$@ echo "[deps] "$@

View File

@ -1,7 +1,7 @@
module Date module Date
(Date (..), (Date (..),
Day (..), dayNum, exact, Day (..), dayNum, exact,
formatLong, formatShort, formatRSS, formatSlash, formatTooltip, formatLong, formatShort, formatRSS, formatSlash,
parseP, parseS, parseA) parseP, parseS, parseA)
where where
@ -9,7 +9,7 @@ import Control.Applicative
import qualified Text.ParserCombinators.ReadP as ReadP import qualified Text.ParserCombinators.ReadP as ReadP
import Text.ParserCombinators.ReadP (ReadP, readS_to_P, readP_to_S, (<++)) import Text.ParserCombinators.ReadP (ReadP, readS_to_P, readP_to_S, (<++))
import Data.Time hiding (Day) import Data.Time hiding (Day)
import Data.Char (isSpace, toLower) import Data.Char (isSpace)
import BuilderQQ import BuilderQQ
import Data.Function (on) import Data.Function (on)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
@ -60,15 +60,6 @@ formatShort (Date {month, day}) = [b|$day'$month'|] where
Unknown -> "" Unknown -> ""
month' = formatTime defaultTimeLocale "%b" $ fromGregorian 1 month 1 month' = formatTime defaultTimeLocale "%b" $ fromGregorian 1 month 1
formatTooltip :: Date -> Builder
formatTooltip (Date {year, month, day}) = [b|$day'$month' $year|] where
day' = case day of
Exact d -> [b|$d $&|]
Approx d -> [b|$d? $&|]
Unknown -> ""
month' = map toLower $
formatTime defaultTimeLocale "%b" $ fromGregorian 1 month 1
formatRSS :: Date -> Builder formatRSS :: Date -> Builder
formatRSS = fromString . format . toTime where formatRSS = fromString . format . toTime where
format = formatTime defaultTimeLocale "%a, %d %b %_Y %T GMT" format = formatTime defaultTimeLocale "%a, %d %b %_Y %T GMT"

View File

@ -29,17 +29,22 @@ dependSingle' yamlDir indexFile info prefix build nsfw =
where where
images = #all if nsfw then #images info else #sfwImages info images = #all if nsfw then #images info else #sfwImages info
paths = map #path images paths = map #path images
thumb = thumbFile $ thumbnail info
jxls fs = [base <.> "jxl" | f <- fs,
let (base, ext) = splitExtension f,
ext `elem` imgTypes]
imgTypes = words ".png .jpg .gif"
imgFiles0 = thumb : map pageFile paths ++ map bigFile paths
imgFiles = imgFiles0 ++ jxls imgFiles0
dls = mapMaybe #download images dls = mapMaybe #download images
extras = #extras info extras = #extras info
dir = build </> prefix </> yamlDir dir = build </> prefix </> yamlDir
page = dir </> "index.html" page = dir </> "index.html"
deps = unwords $ map (dir </>) $ deps = unwords $ map (dir </>) $ imgFiles ++ dls ++ extras
thumbFile (thumbnail info) :
map pageFile paths ++
map bigFile paths ++
dls ++ extras
dependGallery :: GalleryInfo dependGallery :: GalleryInfo
-> FilePath -- ^ index file -> FilePath -- ^ index file

View File

@ -12,10 +12,10 @@ import Data.Function (on, (&))
import qualified Data.HashMap.Strict as HashMap import qualified Data.HashMap.Strict as HashMap
import Data.HashSet (HashSet) import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet import qualified Data.HashSet as HashSet
import Data.List (intersperse, groupBy, sortBy, sort) import Data.List (intersperse, groupBy, sortBy, sortOn)
import Data.Maybe import Data.Maybe
import qualified Data.Text.Lazy as Lazy import qualified Data.Text.Lazy as Lazy
import System.FilePath (takeDirectory, joinPath, splitPath) import System.FilePath (takeDirectory, joinPath, splitPath, (-<.>))
import GHC.Exts (Down (..), the) import GHC.Exts (Down (..), the)
make :: Text -> GalleryInfo -> [(FilePath, Info)] -> Lazy.Text make :: Text -> GalleryInfo -> [(FilePath, Info)] -> Lazy.Text
@ -52,8 +52,9 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
<div class=page> <div class=page>
<header> <header>
<h1>$title</h1> <h1>$title</h1>
<a class="right corner" href=rss.xml>rss</a> <h2 class="right corner">
<a class="left corner" href=$undir>back</a> <a href=rss.xml>rss</a>
</h2>
</header> </header>
<nav id=filters> <nav id=filters>
@ -85,6 +86,10 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
$6.items $6.items
</ul> </ul>
</main> </main>
<footer>
<a href=$undir>all galleries</a>
</footer>
</div> </div>
|] |]
where where
@ -105,7 +110,7 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
allTags = infos allTags = infos
& concatMap (map (,1) . tagsFor nsfw . #second) & concatMap (map (,1) . tagsFor nsfw . #second)
& HashMap.fromListWith (+) & HashMap.toList & HashMap.fromListWith (+) & HashMap.toList
& sort & sortOn (\(tag, count) -> (Down count, tag))
requireFilters = map (uncurry $ makeFilter "require" mempty) allTags requireFilters = map (uncurry $ makeFilter "require" mempty) allTags
excludeFilters = map (uncurry $ makeFilter "exclude" hidden) allTags excludeFilters = map (uncurry $ makeFilter "exclude" hidden) allTags
@ -148,25 +153,30 @@ makeYearItems nsfw year infos = [b|@0
makeItem :: Bool -> FilePath -> Info -> Builder makeItem :: Bool -> FilePath -> Info -> Builder
makeItem nsfw file info@(Info {bg}) = [b|@0 makeItem nsfw file info@(Info {bg}) = [b|@0
<li class="item post$nsfw'" data-year=$year' data-updated="$updated'" <li class="item post$nsfw'" data-date="$date'" data-year=$year'
data-updated="$updated'"
data-tags="$tags'"> data-tags="$tags'">
<a href="$dir"> <figure>
<img src="$thumb" loading=lazy$bgStyle <a href="$dir">
width=200 height=200 <picture>
title="$tooltip"> <source srcset="$thumbJxl" type=image/jxl>
</a> <img src="$thumb" loading=lazy$bgStyle>
</picture>
</a>
<figcaption>
<span class=date>$date'</span>
<span class=title>$title</span>
</figcaption>
</figure>
|] |]
where where
title = fromMaybe (#title info) $ #galleryTitle info title = fromMaybe (#title info) $ #galleryTitle info
dir = takeDirectory file dir = takeDirectory file
thumb = getThumb dir info thumb = getThumb dir info; thumbJxl = thumb -<.> "jxl"
nsfw' = if nsfw && #anyNsfw info then [b| nsfw|] else "" nsfw' = if nsfw && #anyNsfw info then [b| nsfw|] else ""
tags' = fold $ intersperse ";" $ map fromText $ tagsFor nsfw info tags' = fold $ intersperse ";" $ map fromText $ tagsFor nsfw info
date = #latestDate info nsfw date = #latestDate info nsfw
date' = formatTooltip date date' = formatShort date
year' = #year date year' = #year date
updated' = if #updated info nsfw then [b|true|] else [b|false|] updated' = if #updated info nsfw then [b|true|] else [b|false|]
bgStyle = case bg of Other col -> [b| style="background: $col"|]; _ -> "" bgStyle = ifJust bg \col -> [b| style="background: $col"|]
tooltip =
let upd = if #updated info nsfw then "updated " else "" :: Builder in
[b|$title ($upd$date')|]

View File

@ -1,12 +1,9 @@
{-# OPTIONS_GHC -fdefer-typed-holes #-}
{-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-orphans #-}
module Info module Info
(Info (..), (Info (..),
tagsFor, descFor, imagesFor, linksFor, updatesFor, lastUpdate, tagsFor, descFor, imagesFor, linksFor, updatesFor, compareFor, sortFor,
compareFor, sortFor,
Artist (..), Images' (..), Images, Image (..), Desc (..), DescField (..), Artist (..), Images' (..), Images, Image (..), Desc (..), DescField (..),
Link (..), Update (..), Bg (..), Link (..), Update (..),
GalleryInfo (..), GalleryFilters (..), ArtistFilter (..), NsfwFilter (..), GalleryInfo (..), GalleryFilters (..), ArtistFilter (..), NsfwFilter (..),
IndexInfo (..), IndexInfo (..),
readArtistFilter, matchArtist, readNsfwFilter, matchNsfw, matchFilters, readArtistFilter, matchArtist, readNsfwFilter, matchNsfw, matchFilters,
@ -36,7 +33,7 @@ import Data.Text (Text)
import qualified Data.Text as Text import qualified Data.Text as Text
import Data.YAML (FromYAML (..), (.:), (.:?), (.!=)) import Data.YAML (FromYAML (..), (.:), (.:?), (.!=))
import qualified Data.YAML as YAML import qualified Data.YAML as YAML
import System.FilePath ((</>), takeBaseName, takeExtension, splitExtension) import System.FilePath ((</>), takeBaseName, takeExtension, splitExtensions)
import Data.Bifunctor (second) import Data.Bifunctor (second)
@ -60,7 +57,7 @@ data Info =
nsfwTags :: ![Text], nsfwTags :: ![Text],
desc :: !Desc, desc :: !Desc,
nsfwDesc :: !Desc, nsfwDesc :: !Desc,
bg :: !Bg, bg :: !(Maybe Text),
images :: !Images, images :: !Images,
thumb' :: !(Maybe FilePath), thumb' :: !(Maybe FilePath),
links :: ![Link], links :: ![Link],
@ -68,9 +65,6 @@ data Info =
} }
deriving (Eq, Show) deriving (Eq, Show)
data Bg = Default | NoBorder | Other !Text
deriving (Eq, Show)
data Desc = data Desc =
NoDesc NoDesc
| TextDesc !Text | TextDesc !Text
@ -220,10 +214,6 @@ linksFor nsfw = if nsfw then #links else #sfwLinks
updatesFor :: Bool -> Info -> [(Date, [Update])] updatesFor :: Bool -> Info -> [(Date, [Update])]
updatesFor nsfw = if nsfw then #updates else #sfwUpdates updatesFor nsfw = if nsfw then #updates else #sfwUpdates
lastUpdate :: Bool -> Info -> Maybe Date
lastUpdate nsfw info =
case updatesFor nsfw info of [] -> Nothing; us -> Just $ fst $ last us
compareFor :: Bool -> Info -> Info -> Ordering compareFor :: Bool -> Info -> Info -> Ordering
compareFor nsfw = comparing \i -> (#latestDate i nsfw, #sortEx i, #title i) compareFor nsfw = comparing \i -> (#latestDate i nsfw, #sortEx i, #title i)
@ -254,7 +244,7 @@ bigFile f
addSuffix :: String -> FilePath -> FilePath addSuffix :: String -> FilePath -> FilePath
addSuffix suf path = addSuffix suf path =
let (pre, ext) = splitExtension path in let (pre, ext) = splitExtensions path in
pre ++ suf ++ ext pre ++ suf ++ ext
@ -289,18 +279,12 @@ instance FromYAML Info where
<*> m .:? "nsfw-tags" .!= [] <*> m .:? "nsfw-tags" .!= []
<*> m .:? "desc" .!= NoDesc <*> m .:? "desc" .!= NoDesc
<*> m .:? "nsfw-desc" .!= NoDesc <*> m .:? "nsfw-desc" .!= NoDesc
<*> m .:? "bg" .!= Default <*> m .:? "bg"
<*> m .: "images" <*> m .: "images"
<*> m .:? "thumb" <*> m .:? "thumb"
<*> m .:? "links" .!= [] <*> m .:? "links" .!= []
<*> m .:? "extras" .!= [] <*> m .:? "extras" .!= []
instance FromYAML Bg where
parseYAML y =
YAML.withNull "default value" (pure Default) y
<|> YAML.withStr "css <image> or \"noborder\""
(\str -> pure if str == "noborder" then NoBorder else Other str) y
instance FromYAML Artist where instance FromYAML Artist where
parseYAML y = justName y <|> withUrl y where parseYAML y = justName y <|> withUrl y where
justName = YAML.withStr "name" \name -> pure $ Artist {name, url = Nothing} justName = YAML.withStr "name" \name -> pure $ Artist {name, url = Nothing}

View File

@ -7,7 +7,7 @@ data What = Single | Gallery
instance CanBuild What where instance CanBuild What where
build Single = "this art" build Single = "this art"
build Gallery = "some of the art in this gallery" build Gallery = "the art in this gallery"
script :: Maybe What -> Builder script :: Maybe What -> Builder
@ -34,9 +34,7 @@ dialog (Just what) = [b|@0
<div class=dialog-buttons> <div class=dialog-buttons>
<button id=nsfw-yes class=yes>i am an adult</button> <button id=nsfw-yes class=yes>i am an adult</button>
<a href=//crouton.net referrerpolicy=no-referrer> <button id=nsfw-no class=no>i am not</button>
<button id=nsfw-no class=no>i am not</button>
</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -12,7 +12,7 @@ import Data.List (sort, intersperse)
import Data.Maybe (fromMaybe, isNothing, isJust) import Data.Maybe (fromMaybe, isNothing, isJust)
import qualified Data.Text as Strict import qualified Data.Text as Strict
import qualified Data.Text.Lazy as Lazy import qualified Data.Text.Lazy as Lazy
import System.FilePath (joinPath, splitPath) import System.FilePath (joinPath, splitPath, (-<.>))
import qualified Data.HashSet as Set import qualified Data.HashSet as Set
import Data.Traversable import Data.Traversable
@ -57,6 +57,7 @@ make' root siteName prefix nsfw _dataDir dir
let download0 = fromMaybe (bigFile path0) download0' let download0 = fromMaybe (bigFile path0) download0'
let path0' = pageFile path0 let path0' = pageFile path0
let path0'Jxl = path0' -<.> "jxl"
let descSection = makeDesc $ descFor nsfw info let descSection = makeDesc $ descFor nsfw info
let tagsList = makeTags undir $ tagsFor nsfw info let tagsList = makeTags undir $ tagsFor nsfw info
@ -84,20 +85,9 @@ make' root siteName prefix nsfw _dataDir dir
| otherwise = mempty | otherwise = mempty
let warningT = makeWarning [b|.|] let warningT = makeWarning [b|.|]
let bgStyle = case bg of let bgStyle = ifJust bg \col -> [b|@0
Default -> "" <style> #mainfig { background: $col; } </style>
NoBorder -> [b|@0 |]
<style>
#mainfig {
background: transparent;
border: none;
box-shadow: none;
}
</style>
|]
Other col -> [b|@0
<style> #mainfig { background: $col; } </style>
|]
let url = [b|$root/$prefix/$dir|] let url = [b|$root/$prefix/$dir|]
let desc = case artist of let desc = case artist of
@ -154,13 +144,10 @@ make' root siteName prefix nsfw _dataDir dir
<div class=page> <div class=page>
<header> <header>
<h1>$title</h1> <h1>$title</h1>
$artistTag
<h2 id=date class="right corner"> <h2 id=date class="right corner">
$formattedDate $updateDate $formattedDate $updateDate
</h2> </h2>
<h2 class="left corner">
$artistTag
<a href=$undir>back to gallery</a>
</h2>
</header> </header>
$2.buttonBar $2.buttonBar
@ -169,7 +156,10 @@ make' root siteName prefix nsfw _dataDir dir
<figure id=mainfig> <figure id=mainfig>
$warning' $warning'
<a id=mainlink href="$download0" title="download full version"> <a id=mainlink href="$download0" title="download full version">
<img id=mainimg src="$path0'" alt=""> <picture id=mainimg>
<source srcset="$path0'Jxl" type=image/jxl>
<img src="$path0'" alt="">
</picture>
</a> </a>
</figure> </figure>
@ -183,6 +173,10 @@ make' root siteName prefix nsfw _dataDir dir
$6.tagsList $6.tagsList
</div> </div>
</main> </main>
<footer>
<a href=$undir>back to gallery</a>
</footer>
</div> </div>
<template id=cw-template> <template id=cw-template>
@ -195,7 +189,7 @@ last' xs = if null xs then Nothing else Just $ last xs
makeArtist :: Artist -> Builder makeArtist :: Artist -> Builder
makeArtist (Artist {name, url}) = makeArtist (Artist {name, url}) =
[b|by $artistLink <br>|] [b|<h2 id=artist class="left corner">by $artistLink</h2>|]
where where
artistLink = case url of artistLink = case url of
Just u -> [b|<a href="$u">$name</a>|] Just u -> [b|<a href="$u">$name</a>|]
@ -313,7 +307,7 @@ extLinks links =
if null links then "" else [b|@0 if null links then "" else [b|@0
<nav id=links class=info-section> <nav id=links class=info-section>
<h2>links</h2> <h2>links</h2>
<ul> <ul class="buttonbar bb-links">
$4.linkList $4.linkList
</ul> </ul>
</nav> </nav>

View File

@ -35,14 +35,6 @@ function updateItems() {
document.getElementById(`marker-${year}`).hidden = hide; document.getElementById(`marker-${year}`).hidden = hide;
} }
function disp(pfx, tags) {
return Array(...tags).map(x => pfx + x).join('\u2003'); // em space
}
let plus = disp('+\u2009', reqTags); // thin space
let minus = disp('-\u2009', excTags);
document.getElementById('filters-details').dataset.filters =
`${plus}\u2003${minus}`.trim();
} }
function update() { function update() {
@ -138,7 +130,7 @@ function sortFilters(cmp) {
sort1('exclude'); sort1('exclude');
} }
function sortFiltersAlpha(e) { function sortFiltersAlpha() {
function getName(x) { function getName(x) {
if (x.nodeType == Node.ELEMENT_NODE) { if (x.nodeType == Node.ELEMENT_NODE) {
return x.getElementsByTagName('input')[0].value; return x.getElementsByTagName('input')[0].value;
@ -147,10 +139,9 @@ function sortFiltersAlpha(e) {
} }
} }
sortFilters((a, b) => getName(a).localeCompare(getName(b))); sortFilters((a, b) => getName(a).localeCompare(getName(b)));
e.preventDefault();
} }
function sortFiltersUses(e) { function sortFiltersUses() {
function getUses(x) { function getUses(x) {
if (x.nodeType == Node.ELEMENT_NODE) { if (x.nodeType == Node.ELEMENT_NODE) {
return parseInt(x.getElementsByTagName('label')[0].dataset.count); return parseInt(x.getElementsByTagName('label')[0].dataset.count);
@ -159,7 +150,6 @@ function sortFiltersUses(e) {
} }
} }
sortFilters((a, b) => getUses(b) - getUses(a)); sortFilters((a, b) => getUses(b) - getUses(a));
e.preventDefault();
} }
@ -170,8 +160,8 @@ function setup() {
} }
let items = Array.from(document.getElementsByClassName('post')); let items = Array.from(document.getElementsByClassName('post'));
itemsByYear = new Map; itemsByYear = new Map;
for (let item of items) { for (let item of items) {
let year = item.dataset.year; let year = item.dataset.year;
if (!itemsByYear.has(year)) itemsByYear.set(year, new Set); if (!itemsByYear.has(year)) itemsByYear.set(year, new Set);

View File

@ -17,19 +17,16 @@ function yes() {
dismiss(); dismiss();
} }
// now just a normal link
/*
function no() { function no() {
document.location = '//crouton.net'; history.go(-1);
} }
*/
function setup() { function setup() {
if (alreadyYes()) { if (alreadyYes()) {
dismiss(); dismiss();
} else { } else {
document.getElementById('nsfw-yes').onclick = yes; document.getElementById('nsfw-yes').onclick = yes;
// document.getElementById('nsfw-no').onclick = no; document.getElementById('nsfw-no').onclick = no;
} }
} }

View File

@ -24,7 +24,7 @@ function addCWListeners(id, caption) {
} }
} }
function setImage(id, src, href, cw) { function setImage(id, src, width, height, href, cw, firstLoad) {
let caption = document.getElementById('cw'); let caption = document.getElementById('cw');
let newCaption; let newCaption;
@ -45,13 +45,20 @@ function setImage(id, src, href, cw) {
mainlink.tabIndex = -1; mainlink.tabIndex = -1;
} }
mainimg.src = src; let jxl = src.replace(/\..*?$/, ".jxl");
mainlink.href = href;
mainimg.querySelector('img').src = src;
mainimg.querySelector('source').srcset = jxl;
mainfig.dataset.width = width;
mainfig.dataset.height = height;
mainlink.href = href;
} }
function activateButton(button, doPush = true) { function activateButton(button, doPush = true, firstLoad = false) {
setImage(button.id, button.value, setImage(button.id, button.value,
button.dataset.link, button.dataset.warning); button.dataset.width, button.dataset.height,
button.dataset.link, button.dataset.warning,
firstLoad);
if (doPush) history.pushState(null, '', '#' + button.id); if (doPush) history.pushState(null, '', '#' + button.id);
} }
@ -70,7 +77,7 @@ function useFragment(firstLoad = false) {
if (button) { if (button) {
id = button.id; id = button.id;
button.checked = true; button.checked = true;
activateButton(button, false); activateButton(button, false, firstLoad);
} }
if (firstLoad) addCWListeners(id, document.getElementById('cw')); if (firstLoad) addCWListeners(id, document.getElementById('cw'));
@ -101,7 +108,7 @@ function setup() {
} }; } };
} }
window.addEventListener('popstate', e => useFragment()); window.addEventListener('popstate', useFragment);
useFragment(true); useFragment(true);
} }

View File

@ -28,6 +28,7 @@
--text-col: white; --text-col: white;
--text-shadow-col: hsl(0, 0%, 0%, 75%); --text-shadow-col: hsl(0, 0%, 0%, 75%);
--text-shadow: 2px 2px 3px var(--text-shadow-col); --text-shadow: 2px 2px 3px var(--text-shadow-col);
--nsfw-sticker-rotate: 15deg;
--focus-box: 0 0 5px hsl(55deg, 60%, 90%, 80%); --focus-box: 0 0 5px hsl(55deg, 60%, 90%, 80%);
--focus-text: hsl(334deg, 87%, 90%); --focus-text: hsl(334deg, 87%, 90%);
@ -52,8 +53,6 @@
margin: 0; margin: 0;
} }
body { margin: 0; }
header { header {
text-align: center; text-align: center;
} }
@ -66,10 +65,12 @@ h3 { font-size: 110%; }
.page { .page {
background: var(--background); background: var(--background);
border: var(--border);
position: relative; position: relative;
padding: 2em 4em; padding: 2em 4em;
margin: 0 auto; margin: 3em auto 3.5em;
border-radius: var(--border-radius);
color: var(--text-col); color: var(--text-col);
text-shadow: var(--text-shadow); text-shadow: var(--text-shadow);
@ -111,7 +112,7 @@ figure > img {
margin: 0; margin: 0;
padding: 0; padding: 0;
position: absolute; position: absolute;
top: 1em; top: 0.5em;
font-size: 100%; font-size: 100%;
font-weight: 500; font-weight: 500;
@ -233,11 +234,6 @@ p {
hyphens: auto; hyphens: auto;
} }
del {
text-decoration: line-through wavy;
text-decoration-thickness: 2px;
}
.threecol { .threecol {
columns: 3; columns: 3;

View File

@ -3,18 +3,13 @@
:root { :root {
--image-size: 200px; --image-size: 200px;
--badge-size: calc(1/4 * var(--image-size)); --gap: 1em;
--gap: 0em;
} }
@media (min-width: 1000px) { @media (min-width: 1000px) {
.page { max-width: 80%; } .page { max-width: 80%; }
} }
.page {
padding: 2em calc(1/4 * var(--image-size));
}
#filters { #filters {
margin: 1em 0 2em 0; margin: 1em 0 2em 0;
} }
@ -23,7 +18,7 @@
display: grid; display: grid;
grid-template-columns: 15% auto; grid-template-columns: 15% auto;
align-items: baseline; align-items: baseline;
gap: 0.75em; grid-gap: 0.75em;
} }
#filters h3 { #filters h3 {
@ -38,11 +33,12 @@
.filterlist { .filterlist {
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
/* justify-content: space-between; */
padding: 0; padding: 0;
font-weight: 500; font-weight: 500;
font-size: 90%; font-size: 90%;
gap: 0.5em; grid-gap: 0.5em 1em;
} }
.filterlist input { .filterlist input {
@ -58,10 +54,10 @@
} }
.filterlist label { .filterlist label {
cursor: pointer;
padding: 0.15em 0.4em; padding: 0.15em 0.4em;
border-radius: 1000px; border-radius: var(--button-radius);
border: 1px solid transparent; background: var(--button-bg);
border: var(--button-border);
} }
.filterlist label::before { .filterlist label::before {
@ -74,10 +70,6 @@
text-shadow: none; text-shadow: none;
} }
.filterlist :checked + label::before { content: url('/style/checked.svg'); } .filterlist :checked + label::before { content: url('/style/checked.svg'); }
.filterlist :checked + label {
background: var(--button-bg);
border: var(--button-border);
}
.filterlist label:not([data-count="1"])::after { .filterlist label:not([data-count="1"])::after {
content: attr(data-count); content: attr(data-count);
@ -101,7 +93,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 0; margin-top: 0;
gap: 2em; grid-gap: 2em;
} }
#filterstuff li { #filterstuff li {
@ -118,12 +110,6 @@
text-decoration: none; text-decoration: none;
} }
#filters-details:not([open])::after {
content: attr(data-filters);
margin-left: 2em;
font-size: 90%;
}
@media (max-width: 80rem) { @media (max-width: 80rem) {
#filters div { #filters div {
@ -138,7 +124,7 @@
#filterstuff { #filterstuff {
grid-area: unset; grid-area: unset;
flex-flow: column; flex-flow: column;
gap: 0.2em; grid-gap: 0.2em;
} }
#filterstuff li { #filterstuff li {
@ -150,8 +136,8 @@
.grid { .grid {
padding: 0; padding: 0;
display: grid; display: grid;
grid: auto-flow / repeat(auto-fit, var(--image-size)); grid-template-columns: repeat(auto-fill, var(--image-size));
gap: var(--gap); grid-gap: var(--gap);
justify-content: center; justify-content: center;
} }
@ -168,18 +154,35 @@
.item:not(.year-marker) { .item:not(.year-marker) {
box-shadow: var(--text-shadow); box-shadow: var(--text-shadow);
outline: var(--border-thickness) solid var(--border-col); border: var(--border-thickness) solid var(--border-col);
background: hsl(0, 0%, 0%, 50%); border-radius: 0.5em;
clip-path: polygon(5% 0, 95% 10%, 95% 100%, 5% 90%); background: hsl(340, 45%, 65%);
}
.item img {
clip-path: polygon(7% 2%, 93% 12%, 93% 98%, 7% 88%);
} }
.item:focus-within { .item:focus-within {
box-shadow: var(--focus-box); box-shadow: var(--focus-box);
} }
figure {
margin: 0;
padding: 0;
}
figcaption .date, figcaption .title {
position: absolute;
width: 100%;
border: 1px solid var(--border-col);
display: block;
text-align: center;
background: hsl(0, 0%, 0%, 75%);
text-shadow: none;
}
figcaption .date { top: -1px; left: -1px; }
figcaption .title { bottom: -1px; left: -1px; }
.date { text-transform: lowercase; }
.year-marker { .year-marker {
/* uncomment to reenable line breaks before year markers */ /* uncomment to reenable line breaks before year markers */
/* grid-area: auto / 1; */ /* grid-area: auto / 1; */
@ -190,7 +193,7 @@
--gap: 0.2em; --gap: 0.2em;
display: grid; display: grid;
grid-template-columns: repeat(2, calc(50% - 3 * var(--gap))); grid-template-columns: repeat(2, calc(50% - 3 * var(--gap)));
gap: var(--gap); grid-gap: var(--gap);
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
@ -207,8 +210,9 @@
} }
.item.nsfw::before, .item[data-updated="true"]::after { .item.nsfw::before, .item[data-updated="true"]::after {
height: var(--badge-size); height: var(--size);
width: var(--badge-size); width: var(--size);
transform: var(--base-transform);
display: inline-block; display: inline-block;
position: absolute; position: absolute;
@ -217,18 +221,19 @@
} }
.item.nsfw::before { .item.nsfw::before {
transform: rotate(15deg); --size: calc(1/4 * var(--image-size));
--base-transform: rotate(var(--nsfw-sticker-rotate));
content: url(../18_plus_white.svg); content: url(../18_plus_white.svg);
top: 11%; top: calc(1em + 3px);
right: 7%; right: 3px;
z-index: 100;
} }
.item[data-updated="true"]::after { .item[data-updated="true"]::after {
transform: rotate(-8deg); --size: calc(1/4 * var(--image-size));
--base-transform: rotate(-8deg);
content: url(../sparkles.svg); content: url(../sparkles.svg);
bottom: 4%; bottom: calc(1em + 3px);
right: 7%; right: 3px;
} }
footer { footer {
@ -237,6 +242,68 @@ footer {
margin-top: 1em; margin-top: 1em;
} }
@media (hover) and (pointer: fine) {
.item:hover .date, .item:hover .title,
.item:hover::before, .item:hover::after {
filter: opacity(20%);
}
@media (prefers-reduced-motion: no-preference) {
figcaption .date, figcaption .title, .item::before, .item::after {
transition-property: filter, transform;
transition-duration: 0.15s;
transition-timing-function: ease-in-out;
}
.item:hover .title {
transform: translate(-20%, 80%) rotateZ(7deg);
}
.item:hover .date {
transform: translate(20%, -80%) rotateZ(7deg);
}
.item:hover::before {
transform: translate(1.5em, -1.5em) var(--base-transform);
}
.item:hover::after {
transform: translate(1.5em, 1.5em) var(--base-transform);
}
}
}
@media (not hover), (pointer: coarse) {
.item:not(.year-marker) {
height: min-content;
}
figcaption .date, figcaption .title {
position: initial;
}
figcaption .date {
border-bottom: none;
}
figcaption .title {
border-top: none;
}
figcaption .date::after {
content: ':';
}
.item a {
display: block;
height: var(--image-size);
}
.item img {
margin-bottom: 0;
}
}
@media (pointer: coarse) { @media (pointer: coarse) {
#filters label { #filters label {
font-size: 150%; font-size: 150%;

View File

@ -9,7 +9,6 @@
.page { .page {
width: 37.5em; width: 37.5em;
border-radius: var(--border-radius);
} }
#title::before, #title::after { #title::before, #title::after {
@ -31,11 +30,12 @@
} }
@media not speech { @media screen {
.nsfw::after { .nsfw::after {
content: url(../18_plus_white.svg); content: url(../18_plus_white.svg);
height: 1em; height: 1em;
width: 1em; width: 1em;
transform: rotate(var(--nsfw-sticker-rotate));
mix-blend-mode: hard-light; mix-blend-mode: hard-light;
margin-left: 0.3em; margin-left: 0.3em;
} }
@ -43,7 +43,7 @@
@media speech { @media speech {
.nsfw::after { .nsfw::after {
content: ' (contains adult content)'; content: ' (some nsfw)';
} }
} }
@ -67,7 +67,7 @@
align-items: center; align-items: center;
justify-content: space-evenly; justify-content: space-evenly;
padding: 0; padding: 0;
gap: 1.5em; grid-gap: 1.5em;
font-size: 175%; font-size: 175%;
} }
@ -93,7 +93,7 @@ main {
.list { .list {
font-size: 300%; font-size: 300%;
grid-template-columns: 100%; grid-template-columns: 100%;
gap: 1em; grid-gap: 1em;
} }
} }

View File

@ -20,7 +20,7 @@
"icon text" "icon text"
"buttons buttons" "buttons buttons"
/ 1fr 3fr; / 1fr 3fr;
gap: 0.5em; grid-gap: 0.5em;
align-items: center; align-items: center;
min-height: 20vh; min-height: 20vh;
@ -61,8 +61,8 @@
margin-top: 1em; margin-top: 1em;
} }
.dialog-buttons > * { .dialog button + button {
margin: 0 1em; margin-left: 1em;
} }
.dialog button { .dialog button {
@ -74,16 +74,6 @@
font-weight: 600; font-weight: 600;
font-size: inherit; font-size: inherit;
color: black; color: black;
position: relative;
cursor: pointer;
}
.dialog button a {
inset: 0;
width: max-content;
text-decoration: none;
} }
.dialog .yes { .dialog .yes {

View File

@ -6,14 +6,8 @@
--image-width: 1000px; --image-width: 1000px;
} }
body {
display: grid;
min-height: 100vh;
}
.page { .page {
max-width: var(--image-width); max-width: var(--image-width);
align-self: center;
} }
#mainfig { #mainfig {
@ -24,6 +18,7 @@ body {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border: var(--border-thickness) solid var(--border-col); border: var(--border-thickness) solid var(--border-col);
border-radius: 1em;
box-shadow: var(--text-shadow); box-shadow: var(--text-shadow);
background: hsl(340, 45%, 65%); background: hsl(340, 45%, 65%);
} }
@ -32,7 +27,7 @@ body {
box-shadow: var(--focus-box); box-shadow: var(--focus-box);
} }
#mainimg { #mainimg, #mainimg img {
display: block; display: block;
} }
@ -81,6 +76,20 @@ body {
} }
} }
/*
.nsfw-label::after {
content: url(../18_plus_white.svg);
display: inline-block;
height: 0.9em; width: 0.9em;
vertical-align: -0.07em;
padding-left: 0.25em;
}
:checked ~ .nsfw-label::after {
content: url(../18_plus.svg);
}
*/
#date { text-transform: lowercase; } #date { text-transform: lowercase; }
#info { #info {
@ -95,6 +104,9 @@ body {
align-items: baseline; align-items: baseline;
} }
/* 'display: contents' removes things from the accessibility tree
* which is probably only a problem for screen readers?
*/
@media not speech { @media not speech {
.info-section { .info-section {
display: contents; display: contents;
@ -102,13 +114,10 @@ body {
} }
#info figure { #info figure {
width: min-content;
margin: 0.25em auto; margin: 0.25em auto;
} }
#info figure img {
max-width: 100%;
}
#info .light-bg { #info .light-bg {
background: hsl(0deg, 0%, 100%, 75%); background: hsl(0deg, 0%, 100%, 75%);
padding: 5px; padding: 5px;
@ -118,7 +127,6 @@ body {
#info .floating { #info .floating {
float: right; float: right;
margin-left: 0.8em; margin-left: 0.8em;
max-width: 40%;
} }
#info .floating.left { #info .floating.left {
@ -144,7 +152,7 @@ body {
#updates dl { #updates dl {
display: grid; display: grid;
grid-template-columns: min-content auto; grid-template-columns: min-content auto;
gap: 0.5em; grid-gap: 0.5em;
align-items: baseline; align-items: baseline;
} }
@ -183,7 +191,7 @@ footer {
margin: 1.5em 0; margin: 1.5em 0;
text-align: center; text-align: center;
display: grid; display: grid;
gap: 0.5em; grid-gap: 0.5em;
grid-template-columns: minmax(auto, 10em) auto minmax(auto, 10em); grid-template-columns: minmax(auto, 10em) auto minmax(auto, 10em);
} }
@ -214,11 +222,29 @@ footer {
grid-area: 1 / 3 / auto / auto; grid-area: 1 / 3 / auto / auto;
} }
:is(#tags, #links) ul { #tags ul {
padding: 0; padding: 0;
} }
:is(#tags, #links) li { #tags li {
display: inline; display: inline;
margin-right: 0.75em; margin-right: 0.75em;
} }
.conversation {
display: grid;
grid-template-columns: min-content auto;
grid-gap: 0.15em 1em;
margin: 0;
}
.conversation > b {
grid-area: auto / 1 / auto / auto;
display: block;
text-align: right;
}
.conversation > q {
grid-area: auto / 2 / auto / auto;
font-style: initial;
quotes: none;
display: block;
}