Compare commits
14 commits
d18c19a2e6
...
7328346502
Author | SHA1 | Date | |
---|---|---|---|
7328346502 | |||
010b590d3f | |||
204c02f219 | |||
7671c1837a | |||
6f3e3a08a3 | |||
87975bb1db | |||
c088389889 | |||
5944a55020 | |||
8e5d30d372 | |||
645424a7d8 | |||
18629cb220 | |||
9d0b1a5eb3 | |||
07011227bc | |||
035aa78920 |
10 changed files with 112 additions and 81 deletions
15
Makefile
15
Makefile
|
@ -16,18 +16,11 @@ MAKEPAGES := $(TMPDIR)/make-pages
|
|||
|
||||
YAMLS != find -L $(DATADIR) -name $(INFONAME)
|
||||
|
||||
TSCRIPTS != find script -name '*.ts'
|
||||
STYLES != find style -name '*.css'
|
||||
STYLESVGS != find style -name '*.svg'
|
||||
STYLEPNGS != find style -name '*.png'
|
||||
TSCRIPTS != find script -name '*.ts'
|
||||
STYLES != find style -type f
|
||||
FONTS != find fonts -type f
|
||||
|
||||
|
||||
FONTS != find fonts \
|
||||
-iname '*.eot' -or -iname '*.svg' -or \
|
||||
-iname '*.ttf' -or -iname '*.woff' -or \
|
||||
-iname '*.woff2' -or -iname '*.css'
|
||||
|
||||
STATIC = $(STYLES) $(STYLEPNGS) $(STYLESVGS) $(FONTS)
|
||||
STATIC = $(STYLES) $(FONTS)
|
||||
BSTATIC = $(patsubst %,$(BUILDDIR)/%,$(STATIC))
|
||||
BSCRIPTS = $(patsubst %.ts,$(BUILDDIR)/%.js,$(TSCRIPTS))
|
||||
|
||||
|
|
|
@ -18,9 +18,15 @@ import Data.List.NonEmpty (NonEmpty, toList)
|
|||
|
||||
data Chunk = Lit String | Var String
|
||||
|
||||
|
||||
-- |
|
||||
-- * use @$var@ to insert a variable (instance of 'CanBuild')
|
||||
-- * use @$&@ to insert nothing like @\&@ in a string (e.g. to add whitespace
|
||||
-- at the start or end, or to have a variable followed by a letter
|
||||
-- * use @$$@ for a literal @$@
|
||||
parseB :: String -> ExpQ
|
||||
parseB = toExpQ . reverse . go "" [] . trim where
|
||||
trim = dropWhileEnd isSpace . dropWhile (== '\n')
|
||||
trim = dropWhileEnd isSpace . dropWhile isSpace
|
||||
go acc cs [] = addLit acc cs
|
||||
go acc cs ('$':'&':rest) = go acc cs rest -- $&: expands to nothing
|
||||
go acc cs ('$':'$':rest) = go ('$' : acc) cs rest -- $$: expands to one $
|
||||
|
@ -58,7 +64,8 @@ escId = foldMap esc1 . Strict.unpack where
|
|||
escAttr :: Strict.Text -> Builder
|
||||
escAttr = foldMap esc1 . Strict.unpack where
|
||||
esc1 c = fromMaybe (singleton c) $ lookup c
|
||||
[('<', "<"), ('>', ">"), ('"', """), ('\'', "'")]
|
||||
[('<', "<"), ('>', ">"), ('"', """), ('\'', "'"),
|
||||
('&', "&")]
|
||||
|
||||
|
||||
class CanBuild a where build :: a -> Builder
|
||||
|
|
|
@ -80,9 +80,7 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|
|
|||
</nav>
|
||||
|
||||
<main>
|
||||
<ul class=grid>
|
||||
$items
|
||||
</ul>
|
||||
$items
|
||||
</main>
|
||||
</div>
|
||||
|]
|
||||
|
@ -129,16 +127,17 @@ makeFilter prefix initial tag count = [b|
|
|||
where
|
||||
id' = [b|$prefix$&_$tag'|]
|
||||
tag' = escId tag
|
||||
checked = if HashSet.member tag initial then [b| checked|] else ""
|
||||
hidden = if count <= 1 then [b| hidden|] else ""
|
||||
checked = if HashSet.member tag initial then [b|$& checked|] else ""
|
||||
hidden = if count <= 1 then [b|$& hidden|] else ""
|
||||
|
||||
makeYearItems :: Bool -- ^ nsfw
|
||||
-> Int -- ^ year
|
||||
-> [(FilePath, Info)]
|
||||
-> Builder
|
||||
makeYearItems nsfw year infos = [b|
|
||||
<li class="item year-marker" id="marker-$year">
|
||||
<span class=year-text>$year'</span>
|
||||
<h2 class="item year-marker" id="marker-$year" aria-label=$year>
|
||||
$year'
|
||||
</h2>
|
||||
$items
|
||||
|]
|
||||
where
|
||||
|
@ -147,24 +146,25 @@ makeYearItems nsfw year infos = [b|
|
|||
|
||||
makeItem :: Bool -> FilePath -> Info -> Builder
|
||||
makeItem nsfw file info@(Info {bg}) = [b|
|
||||
<li class="item post$nsfw'" data-year=$year' data-updated="$updated'"
|
||||
<figure class="item post$nsfw'" data-year=$year' data-updated="$updated'"
|
||||
data-tags="$tags'">
|
||||
<a href="$dir">
|
||||
<img src="$thumbnail" loading=lazy$bgStyle
|
||||
width=200 height=200
|
||||
title="$tooltip">
|
||||
alt="$title" title="$tooltip">
|
||||
</a>
|
||||
</figure>
|
||||
|]
|
||||
where
|
||||
title = fromMaybe info.title $ info.galleryTitle
|
||||
title = escAttr $ fromMaybe info.title info.galleryTitle
|
||||
dir = takeDirectory file
|
||||
thumbnail = getThumb dir info
|
||||
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
|
||||
date = latestDateFor nsfw info
|
||||
date' = formatTooltip date
|
||||
year' = date.year
|
||||
updated' = if hasUpdatesFor nsfw info then [b|true|] else [b|false|]
|
||||
bgStyle = case bg of Other col -> [b| style="background: $col"|]; _ -> ""
|
||||
bgStyle = case bg of Other col -> [b|$& style="background: $col"|]; _ -> ""
|
||||
tooltip = [b|$title ($upd$date')|]
|
||||
where upd = if hasUpdatesFor nsfw info then "updated " else "" :: Builder
|
||||
|
|
|
@ -74,13 +74,13 @@ makeItem :: GalleryInfo -> Builder
|
|||
makeItem (GalleryInfo {title, desc, prefix, filters}) = [b|
|
||||
<li$nsfw><a href=$prefix title="$desc">$title</a></li>
|
||||
|]
|
||||
where nsfw = if hasNsfw filters then [b| class=nsfw|] else ""
|
||||
where nsfw = if hasNsfw filters then [b|$& class=nsfw|] else ""
|
||||
|
||||
makeLink :: Link -> Builder
|
||||
makeLink (Link {title, url, nsfw}) = [b|
|
||||
<li$nsfw'><a href=$url>$title</a>
|
||||
|]
|
||||
where nsfw' = if nsfw then [b| class=nsfw|] else ""
|
||||
where nsfw' = if nsfw then [b|$& class=nsfw|] else ""
|
||||
|
||||
hasNsfw :: GalleryFilters -> Bool
|
||||
hasNsfw (GalleryFilters {nsfw}) = nsfw /= NoNsfw
|
||||
|
|
|
@ -105,6 +105,7 @@ data Image =
|
|||
label :: !Text,
|
||||
path :: !FilePath,
|
||||
download :: !(Maybe FilePath),
|
||||
desc :: !Text,
|
||||
nsfw :: !Bool,
|
||||
warning :: !(Maybe Text),
|
||||
resize :: !Bool
|
||||
|
@ -198,7 +199,7 @@ thumb (Info {thumb', images}) =
|
|||
latestDateFor :: Bool -> Info -> Date
|
||||
latestDateFor nsfw i = maximum $ i.date : mapMaybe relDate (updatesFor nsfw i)
|
||||
where
|
||||
relDate (date, us) = date <$ guard (not $ null us || any (.ignoreSort) us)
|
||||
relDate (date, us) = date <$ guard (not $ null us || all (.ignoreSort) us)
|
||||
|
||||
latestYearFor :: Bool -> Info -> Int
|
||||
latestYearFor nsfw info = (latestDateFor nsfw info).year
|
||||
|
@ -369,17 +370,18 @@ unlabelledImage' label' y = asStr y <|> asObj y
|
|||
where
|
||||
asStr = YAML.withStr "path" \(Text.unpack -> path) ->
|
||||
let label = fromMaybe (pathToLabel path) label' in
|
||||
pure $ Image {label, path, download = Nothing,
|
||||
pure $ Image {label, path, download = Nothing, desc = "",
|
||||
nsfw = False, warning = Nothing, resize = True}
|
||||
asObj = YAML.withMap "image info" \m -> do
|
||||
checkKeys m ["path", "download", "nsfw", "warning", "resize"]
|
||||
checkKeys m ["path", "download", "desc", "nsfw", "warning", "resize"]
|
||||
path <- m .: "path"
|
||||
download <- m .:? "download"
|
||||
desc <- m .:? "desc" .!= ""
|
||||
nsfw <- m .:? "nsfw" .!= False
|
||||
warning <- m .:? "warning"
|
||||
resize <- m .:? "resize" .!= True
|
||||
let label = fromMaybe (pathToLabel path) label'
|
||||
pure $ Image {label, path, download, nsfw, warning, resize}
|
||||
pure $ Image {label, path, download, nsfw, warning, desc, resize}
|
||||
pathToLabel = Text.pack . gapToSpace . takeBaseName
|
||||
gapToSpace = map \case '-' -> ' '; '_' -> ' '; c -> c
|
||||
|
||||
|
@ -407,14 +409,13 @@ instance FromYAML Link where
|
|||
updateList :: Maybe (YAML.Node YAML.Pos) ->
|
||||
YAML.Parser [(Date, NonEmpty Update)]
|
||||
updateList =
|
||||
maybe (pure []) $ YAML.withMap "updates" $
|
||||
nonEmptys <=< traverse bodies . Map.toList
|
||||
maybe (pure []) $ YAML.withMap "updates" $ traverse bodies . Map.toList
|
||||
where
|
||||
bodies (date', rest) = (,) <$> parseYAML date' <*> body rest
|
||||
body b =
|
||||
return <$> body1 b
|
||||
<|> YAML.withSeq "update list" (traverse body1) b
|
||||
body b = return <$> body1 b <|> YAML.withSeq "update list" (bodyN b) b
|
||||
body1 b = asDesc b <|> asObj b
|
||||
bodyN y =
|
||||
maybe (YAML.typeMismatch "non-empty list" y) (traverse body1) . nonEmpty
|
||||
asDesc = YAML.withStr "desc" \desc ->
|
||||
pure $ Update {desc, nsfw = False, ignoreSort = False}
|
||||
asObj = YAML.withMap "update info" \m -> do
|
||||
|
@ -423,8 +424,6 @@ updateList =
|
|||
nsfw <- m .:? "nsfw" .!= False
|
||||
ignoreSort <- m .:? "ignore-sort" .!= False
|
||||
pure $ Update {desc, nsfw, ignoreSort}
|
||||
nonEmptys = traverse $ traverse $
|
||||
maybe (fail "expected non-empty list") pure . nonEmpty
|
||||
|
||||
|
||||
data GalleryInfo =
|
||||
|
|
|
@ -26,11 +26,12 @@ make' :: Strict.Text -> Strict.Text -> GalleryInfo
|
|||
-> Maybe FilePath -> [(FilePath, Info)] -> Builder
|
||||
make' root name ginfo@(GalleryInfo {title, desc, prefix}) output infos = [b|
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title>$name—$title</title>
|
||||
<title><![CDATA[$name—$title]]></title>
|
||||
<link>$link</link>
|
||||
<description>$desc</description>
|
||||
<description><![CDATA[$desc]]></description>
|
||||
$selflink
|
||||
|
||||
$items
|
||||
|
@ -50,11 +51,12 @@ make' root name ginfo@(GalleryInfo {title, desc, prefix}) output infos = [b|
|
|||
makeItem :: Strict.Text -> FilePath -> Bool -> FilePath -> Info -> Builder
|
||||
makeItem root prefix nsfw path info@(Info {title}) = [b|
|
||||
<item>
|
||||
<title>$title$suffix</title>
|
||||
<title><![CDATA[$title$suffix]]></title>
|
||||
<link>$link</link>
|
||||
<guid>$link</guid>
|
||||
$body
|
||||
<dc:creator><![CDATA[$creator]]></dc:creator>
|
||||
<pubDate>$date</pubDate>
|
||||
$body
|
||||
</item>
|
||||
|]
|
||||
where
|
||||
|
@ -77,20 +79,37 @@ makeItem root prefix nsfw path info@(Info {title}) = [b|
|
|||
dir = takeDirectory path
|
||||
link = [b|$root/$prefix/$dir|]
|
||||
|
||||
creator = maybe "niss" (.name) info.artist
|
||||
|
||||
date = formatRSS $ latestDateFor nsfw info
|
||||
artist = ifJust info.artist \case
|
||||
Artist name Nothing -> [b|<p>by $name|]
|
||||
Artist name (Just url) -> [b|<p>by <a href="$url">$name</a>|]
|
||||
desc = makeDesc $ descFor nsfw info
|
||||
|
||||
image = case previewImage info of
|
||||
Just (PFull img) -> figure $ pageFile img
|
||||
Just (PThumb th) -> figure $ thumbFile th
|
||||
preview = previewImage info
|
||||
image = case preview of
|
||||
Just (PFull img) -> figure (escAttr img.desc) $ pageFile img
|
||||
Just (PThumb th) -> figure "full image hidden" $ thumbFile th
|
||||
Nothing -> ""
|
||||
figure p = [b|<figure> <a href="$link"><img src="$link/$p"></a> </figure>|]
|
||||
msg = case preview of
|
||||
Just (PThumb _) -> "<p>(full image hidden; open to see)</p>"
|
||||
_ -> "" :: Text
|
||||
figure alt p = [b|
|
||||
<figure aria-describedby=mainimg>
|
||||
<a href="$link">
|
||||
<img id=mainimg src="$link/$p" alt="$alt" title="$alt">
|
||||
</a>
|
||||
</figure>
|
||||
$msg
|
||||
|]
|
||||
|
||||
makeDesc :: Desc -> Builder
|
||||
makeDesc NoDesc = ""
|
||||
makeDesc (TextDesc txt) = [b|$txt|]
|
||||
makeDesc (LongDesc fs) = [b|<dl>$fields</dl>|]
|
||||
where fields = map (\(DescField {name, text}) -> [b|<dt>$name <dd>$text|]) fs
|
||||
makeDesc (LongDesc fs) = [b|<ul>$fields</ul>|] where
|
||||
fields = map mkField fs
|
||||
mkField (DescField {name, text}) = [b|
|
||||
<li> <b>$name</b>:
|
||||
$text
|
||||
|]
|
||||
|
|
|
@ -17,6 +17,7 @@ import qualified Data.HashSet as Set
|
|||
import Data.Traversable
|
||||
import Data.Semigroup
|
||||
import Data.List.NonEmpty (toList)
|
||||
import Data.Char (isSpace)
|
||||
|
||||
|
||||
-- | e.g. only nsfw images are present for a non-nsfw page
|
||||
|
@ -95,6 +96,8 @@ make' root siteName prefix nsfw _dataDir dir
|
|||
Just (Artist {name}) -> [b|by $name|]
|
||||
Nothing -> "by niss"
|
||||
|
||||
let alt = escAttr image0.desc
|
||||
|
||||
let updateDate = ifJust (last' updates) \(d, _) ->
|
||||
let updated = formatLong d in
|
||||
[b|<br> <span class=updated>updated $updated</span>|]
|
||||
|
@ -104,23 +107,25 @@ make' root siteName prefix nsfw _dataDir dir
|
|||
let nsfwDialog = NsfwWarning.dialog nsfw'
|
||||
|
||||
let imageMeta = case previewImage info of
|
||||
Just (PFull (Image {path})) -> [b|
|
||||
Just (PFull (pageFile -> path)) -> [b|
|
||||
<meta property=og:image content="$url/$path">
|
||||
<meta name=twitter:card content=summary_large_image>
|
||||
<meta name=twitter:image content="$url/$path">
|
||||
|]
|
||||
Just (PThumb path) -> [b|
|
||||
Just (PThumb (thumbFile -> path)) -> [b|
|
||||
<meta property=og:image content="$url/$path">
|
||||
<meta name=twitter:card content=summary>
|
||||
|]
|
||||
Nothing -> throw $ NoThumb dir
|
||||
|
||||
let escTitle = escAttr title
|
||||
|
||||
pure [b|
|
||||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content="width=1200,viewport-fit=cover">
|
||||
<link rel=stylesheet href=/style/shiny/single.css>
|
||||
<link rel=stylesheet href=/style/shiny/single.css blocking=render>
|
||||
<link rel=icon href=/style/niss.svg>
|
||||
|
||||
<meta property=og:type content=article>
|
||||
|
@ -138,13 +143,13 @@ make' root siteName prefix nsfw _dataDir dir
|
|||
|
||||
$prefetches
|
||||
|
||||
<title>$title</title>
|
||||
<title>$escTitle</title>
|
||||
|
||||
$nsfwDialog
|
||||
|
||||
<div class=page>
|
||||
<header>
|
||||
<h1>$title</h1>
|
||||
<h1>$escTitle</h1>
|
||||
<h2 id=date class="right corner">
|
||||
$formattedDate $updateDate
|
||||
</h2>
|
||||
|
@ -156,10 +161,10 @@ make' root siteName prefix nsfw _dataDir dir
|
|||
$buttonBar
|
||||
|
||||
<main>
|
||||
<figure id=mainfig>
|
||||
<figure id=mainfig aria-labelledby=mainimg>
|
||||
$warning'
|
||||
<a id=mainlink href="$download0" title="download full version">
|
||||
<img id=mainimg src="$path0'" alt="">
|
||||
<img id=mainimg src="$path0'" alt="$alt" title="$alt">
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
|
@ -297,16 +302,17 @@ altButton :: Image -> Text -> Builder
|
|||
altButton img i = [b|
|
||||
<li$nsfwClass>
|
||||
<input type=radio name=variant id="$i" value="$path'"
|
||||
data-link="$link"$warning'>
|
||||
data-link="$link"$warning' data-alt="$alt">
|
||||
<label for="$i"$nsfwLabelClass>$label</label>
|
||||
|]
|
||||
where
|
||||
Image {label, nsfw, warning, download} = img
|
||||
nsfwClass = if nsfw then [b| class=nsfw|] else ""
|
||||
nsfwLabelClass = if nsfw then [b| class=nsfw-label|] else ""
|
||||
nsfwClass = if nsfw then [b|$& class=nsfw|] else ""
|
||||
nsfwLabelClass = if nsfw then [b|$& class=nsfw-label|] else ""
|
||||
path' = pageFile img
|
||||
link = fromMaybe (bigFile img) download
|
||||
warning' = ifJust warning \(escAttr -> w) -> [b| data-warning="$w"|]
|
||||
warning' = ifJust warning \(escAttr -> w) -> [b|$& data-warning="$w"|]
|
||||
alt = img.desc
|
||||
|
||||
makeTags :: FilePath -> [Strict.Text] -> Builder
|
||||
makeTags undir tags =
|
||||
|
@ -361,4 +367,8 @@ makeUpdate date ups = [b|
|
|||
<dd>$desc
|
||||
|] where
|
||||
date' = formatSlash date
|
||||
desc = mconcat $ map fromText $ intersperse "; " $ toList $ fmap (.desc) ups
|
||||
desc = mconcat $
|
||||
intersperse "; " $
|
||||
map (fromText . Strict.dropWhileEnd isSpace) $
|
||||
toList $
|
||||
fmap (.desc) ups
|
||||
|
|
|
@ -106,14 +106,18 @@ function makeFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
type Shortcuts = { [short: string]: Set<string> };
|
||||
const shortcuts: Shortcuts = {
|
||||
'summary': new Set('require_artsummary'),
|
||||
'colourexamples': new Set('require_colourexample'),
|
||||
'flatexamples': new Set('require_flatexample'),
|
||||
'sketchexamples': new Set('require_sketchexample'),
|
||||
'iconexamples': new Set('require_iconexample'),
|
||||
'curated': new Set('require_curated')
|
||||
summary: new Set(['require_artsummary']),
|
||||
colourexamples: new Set(['require_colourexample']),
|
||||
flatexamples: new Set(['require_flatexample']),
|
||||
sketchexamples: new Set(['require_sketchexample']),
|
||||
iconexamples: new Set(['require_iconexample']),
|
||||
curated: new Set(['require_curated']),
|
||||
gecs: new Set(['require_niss', 'require_nisse']),
|
||||
qt: new Set(['require_q_t_']),
|
||||
kesi: new Set(['require_kesi']),
|
||||
};
|
||||
|
||||
function useFragment() {
|
||||
|
@ -195,12 +199,11 @@ function setup() {
|
|||
itemsByYear = new Map;
|
||||
for (const item of items) {
|
||||
const year = item.dataset.year;
|
||||
if (year !== undefined) {
|
||||
if (!itemsByYear.has(year)) {
|
||||
itemsByYear.set(year, new Set([item]));
|
||||
} else {
|
||||
itemsByYear.get(year)?.add(item);
|
||||
}
|
||||
if (!year) continue;
|
||||
if (!itemsByYear.has(year)) {
|
||||
itemsByYear.set(year, new Set([item]));
|
||||
} else {
|
||||
itemsByYear.get(year)!.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ function addCWListeners(id: string | null, caption: HTMLElement): void {
|
|||
e => { if (e.key == 'Enter') openCW(id, caption, true) });
|
||||
}
|
||||
|
||||
function setImage(id: string, src: string, href: string, cw: string): void {
|
||||
function setImage(id: string, src: string, href: string,
|
||||
alt: string, cw: string): void {
|
||||
const curCw = document.getElementById('cw');
|
||||
const coverNew = cw != '' && !opened.has(id) && !skipAll.checked;
|
||||
|
||||
|
@ -45,12 +46,14 @@ function setImage(id: string, src: string, href: string, cw: string): void {
|
|||
// else no cover before or after
|
||||
|
||||
mainimg.src = src;
|
||||
mainimg.alt = mainimg.title = alt;
|
||||
mainlink.href = href;
|
||||
}
|
||||
|
||||
function activateButton(button: HTMLInputElement, doPush = true): void {
|
||||
setImage(button.id, button.value,
|
||||
button.dataset.link!,
|
||||
button.dataset.alt ?? '',
|
||||
button.dataset.warning ?? '');
|
||||
|
||||
if (doPush) history.pushState(null, '', '#' + button.id);
|
||||
|
|
|
@ -154,7 +154,7 @@
|
|||
}
|
||||
|
||||
|
||||
.grid {
|
||||
main {
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid: auto-flow / repeat(auto-fit, var(--image-size));
|
||||
|
@ -169,6 +169,7 @@
|
|||
width: var(--image-size);
|
||||
height: var(--image-size);
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
@ -185,17 +186,13 @@
|
|||
box-shadow: var(--focus-box);
|
||||
}
|
||||
|
||||
.year-marker {
|
||||
.year-marker:not([hidden]) {
|
||||
/* uncomment to reenable line breaks before year markers */
|
||||
/* grid-area: auto / 1; */
|
||||
padding: var(--border-thickness);
|
||||
}
|
||||
|
||||
.year-text {
|
||||
--gap: 0.2em;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, calc(50% - 3 * var(--gap)));
|
||||
grid-gap: var(--gap);
|
||||
grid-gap: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue