From 94f8fa22b7a6b20a90126b1a7e8e5f5e7e1dedea Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:16:12 +0200 Subject: [PATCH 1/8] Screenshot tooling (for development) Document the full screenshot loop in shoot.py --- flake.nix | 8 ++++ tools/screenshot/shoot.py | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100755 tools/screenshot/shoot.py diff --git a/flake.nix b/flake.nix index c6c5de41..c5eaf976 100644 --- a/flake.nix +++ b/flake.nix @@ -32,9 +32,17 @@ pkgs.ghc pkgs.haskell-language-server + # Screenshotting pages during design work. + (pkgs.python3.withPackages (ps: [ ps.playwright ])) + # System. pkgs.zlib ]; + + env = { + PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}"; + PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = "true"; + }; }; }; flake = { }; diff --git a/tools/screenshot/shoot.py b/tools/screenshot/shoot.py new file mode 100755 index 00000000..dd7c5fdd --- /dev/null +++ b/tools/screenshot/shoot.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Screenshot pages of the built site for visual review. + +Needs the Nix dev shell, which provides python3 with playwright and the +browsers (`nix develop`, or automatically via direnv). + +Full loop — generate the site, build the real CSS over it, serve, shoot: + + cabal run site -- build && (cd tools/tailwind && npm run build) + cabal run site -- server & # http://127.0.0.1:8000 + ./tools/screenshot/shoot.py /tmp/shots --slice 1100 home=/ news=/news/ + ./tools/screenshot/shoot.py /tmp/shots --width 390 --slice 900 mhome=/ + +After editing templates or CSS, repeat the first line — `site server` only +serves `_site`, it does not rebuild, so it will happily keep serving stale +pages. There is no need to restart it. + +Serve with `server`, not `watch`. `watch` recompiles on change, and Hakyll's +rule for `assets/css/tailwind.css` concatenates the checked-in `dev.css` +snapshot (so that contributors without Node get a styled site), which +overwrites what `npm run build` just wrote into `_site`. Since `dev.css` is a +*snapshot*, it lacks any utility class you have only just used in a template, +so the page silently renders without those classes. See +`.ai/todo/2026-07-26-unify-dev-css-preview.md` for fixing this properly. + +Targets are `name=path` (resolved against --base) or `name=full-url`: + + ./tools/screenshot/shoot.py out/ home=/ news=/news/ + ./tools/screenshot/shoot.py out/ --width 390 --slice 900 news=/news/ + +Without --slice one full-page PNG per target is written; with it the page is cut +into `--slice`-tall pieces (`name-00.png`, `name-01.png`, …), which keeps long +pages legible instead of downscaling them to nothing. +""" + +import argparse +from playwright.sync_api import sync_playwright + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("outdir") + ap.add_argument("targets", nargs="+", metavar="NAME=PATH") + ap.add_argument("--base", default="http://127.0.0.1:8000", + help="server to resolve relative targets against (`cabal run site -- server`)") + ap.add_argument("--width", type=int, default=1440) + ap.add_argument("--slice", type=int, default=0, help="cut into pieces of this height") + ap.add_argument("--settle", type=int, default=1500, help="ms to wait after load") + args = ap.parse_args() + + targets = [(name, url if "://" in url else args.base.rstrip("/") + "/" + url.lstrip("/")) + for name, url in (t.split("=", 1) for t in args.targets)] + viewport = {"width": args.width, "height": args.slice or 1000} + + with sync_playwright() as pw: + browser = pw.chromium.launch() + for name, url in targets: + page = browser.new_page(viewport=viewport) + page.goto(url, wait_until="load") + page.wait_for_timeout(args.settle) + height = page.evaluate("document.documentElement.scrollHeight") + if args.slice: + for i, top in enumerate(range(0, height, args.slice)): + page.screenshot( + path=f"{args.outdir}/{name}-{i:02d}.png", + full_page=True, + clip={"x": 0, "y": top, "width": args.width, + "height": min(args.slice, height - top)}, + ) + else: + page.screenshot(path=f"{args.outdir}/{name}.png", full_page=True) + print(f"{name}: {args.width}x{height}") + page.close() + browser.close() + + +if __name__ == "__main__": + main() From f011aae6e05c3b1f8a5674008ab5909cebfd81ca Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:37:26 +0200 Subject: [PATCH 2/8] Let text utilities override element colours in main.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main.css is not processed by Tailwind and is unlayered, so its element rules beat any utility for the same property. That made `text-gray-300` dead on every dark panel: `p { color: gray-500 }` won, and all dark-panel body copy rendered at 1.9:1 contrast instead of the intended 9.2:1. Move only the `a`/`p` colour declarations into `@layer base`, so utilities (a later layer) win. The `h1`-`h6` rules stay unlayered on purpose. While here: link hover went *lighter* (purple-500, 3.8:1 — fails AA) and `a:visited` used that same lighter colour, so visited links looked de-emphasised. Hover now darkens to purple-900 and `:visited` is dropped. Body copy moves gray-500 -> gray-600 (4.7:1 -> 7.5:1, and 4.4:1 -> 6.8:1 on the gray-100 tiles, which failed AA). --- assets/css/main.css | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/assets/css/main.css b/assets/css/main.css index a4afb548..56a80f23 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -14,30 +14,34 @@ textarea::placeholder { opacity: 1; } -a { - cursor: pointer; - --tw-text-opacity: 1; - color: rgba(109, 40, 217, var(--tw-text-opacity)); -} +/* + * Element defaults for text colour live in `@layer base` so that Tailwind's + * `text-*` utilities (later `utilities` layer) can override them — e.g. the + * light paragraph/link colours on the dark panels. Everything outside this + * block stays unlayered on purpose and therefore beats utilities. + */ +@layer base { + a { + cursor: pointer; + color: #6d28d9; /* purple-700 */ + } -a:hover { - text-decoration: underline; -} + a:hover { + color: #4c1d95; /* purple-900 */ + text-decoration: underline; + } -a:hover, -a:visited { - --tw-text-opacity: 1; - color: rgba(139, 92, 246, var(--tw-text-opacity)); + p { + color: #52525b; /* gray-600 */ + } } .arrow-link { - --tw-border-opacity: 1; - border-color: rgba(109, 40, 217, var(--tw-border-opacity)); + border-color: #6d28d9; } .arrow-link:hover { - --tw-border-opacity: 1; - border-color: rgba(139, 92, 246, var(--tw-border-opacity)); + border-color: #4c1d95; } .arrow-link { @@ -187,11 +191,6 @@ h6 { border-top-width: calc(2px * (1 - var(--tw-divide-y-reverse))); border-bottom-width: calc(2px * var(--tw-divide-y-reverse)); } - -p { - --tw-text-opacity: 1; - color: rgba(113, 113, 122, var(--tw-text-opacity)); -} .youtube-video { position: relative; width: 100%; From fe0e349dc2ebd3f644fc590676ccd849e6de0447 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:38:08 +0200 Subject: [PATCH 3/8] Fix four reported layout annoyances - Homepage: the tile grid butted straight against the hero box (0px gap), because its container had no top margin while the hero has mt-6 sm:mt-12. - News index: ">> Read more" hung 24-48px to the left of the title, because the CTA sat outside the padded block holding the title and teaser. Title, teaser and CTA now share one column; the container gained gutters, so the date heading no longer touches the viewport edge on mobile either. - News and report pages: the date sat in , rendering Maven Pro 700 under a Playfair title. It is now a quiet, non-bold meta line. - Podcast index: the Interlude panel was five centred paragraphs with no spacing between them (Tailwind zeroes p margins and the wrapper had no space-y), plus
for line breaks. It is now one left-aligned block with real paragraph spacing, and the thanks/credits are a smaller second block. Content order changed: "follow the Interlude" now precedes the credits. --- templates/homepage.html | 2 +- templates/news/page.html | 4 +-- templates/news/tile.html | 47 +++++++++++++------------------ templates/podcast/list.html | 55 ++++++++++++++++++++----------------- templates/reports/page.html | 4 +-- 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/templates/homepage.html b/templates/homepage.html index dcfd6daa..01154ddf 100644 --- a/templates/homepage.html +++ b/templates/homepage.html @@ -15,7 +15,7 @@

Amplify Haskell’s impact on humanity. -
+
$if(openreqs)$ $for(openreqs)$ diff --git a/templates/news/page.html b/templates/news/page.html index 968bd111..e26c03ec 100644 --- a/templates/news/page.html +++ b/templates/news/page.html @@ -2,8 +2,8 @@

$title$

-
- $if(author)$$author$, $endif$$date$ +
+ $if(author)$$author$, $endif$$date$
diff --git a/templates/news/tile.html b/templates/news/tile.html index e751eca4..50bb067e 100644 --- a/templates/news/tile.html +++ b/templates/news/tile.html @@ -1,34 +1,25 @@ -
+

$category$

- - $for(news)$ -
-
-
-
-
-

$title$

- $if(author)$ -
- $author$ -
- $endif$ -

$teaser$

-
-
- $if(link)$ - >> Read more - $else$ - >> Read more - $endif$ -
-
-
+ $for(news)$ +
+

$title$

+ $if(author)$ +
+ $author$ +
+ $endif$ +

$teaser$

+
+ $if(link)$ + >> Read more + $else$ + >> Read more + $endif$
$endfor$ diff --git a/templates/podcast/list.html b/templates/podcast/list.html index 968718ba..94e3a967 100644 --- a/templates/podcast/list.html +++ b/templates/podcast/list.html @@ -8,31 +8,36 @@

The Haskell Interlude

-

- The Haskell Interlude is a Haskell-focused podcast where we - interview guests from the Haskell community. The hosts are - Samantha Frohlich, Andres Löh, Matthías Páll Gissurarson, - Mike Sperber, Farhad Mehta, and Jess Foster. -

-

- The music used is "Blue Lambda" by Donya Quick.
- Many thanks to Donya for giving us permission to use this track for our podcast. -

-

- This podcast would not be possible without the help of our Editors, John Zhuang Hui and Jess Foster, and our Quality Inspector, Artin Ghasivand. -

-

- We are very grateful to our previous hosts Alejandro - Serrano, Joachim Breitner, Wouter Swierstra, and Niki Vazou, - as well as to Alp Mestanogullari and Jose Calderon for their editing work and - to Krishna Padmasola, Ben Orchard, and Mihai Maruseac for their transcription work. -

-

- You can also follow the Interlude - on Mastodon, - Bluesky - and X. -

+
+

+ The Haskell Interlude is a Haskell-focused podcast where we + interview guests from the Haskell community. The hosts are + Samantha Frohlich, Andres Löh, Matthías Páll Gissurarson, + Mike Sperber, Farhad Mehta, and Jess Foster. +

+

+ The music used is "Blue Lambda" by + Donya Quick. + Many thanks to Donya for giving us permission to use this track for our podcast. +

+

+ You can also follow the Interlude on + Mastodon, + Bluesky + and X. +

+
+
+

+ This podcast would not be possible without the help of our Editors, John Zhuang Hui and Jess Foster, and our Quality Inspector, Artin Ghasivand. +

+

+ We are very grateful to our previous hosts Alejandro + Serrano, Joachim Breitner, Wouter Swierstra, and Niki Vazou, + as well as to Alp Mestanogullari and Jose Calderon for their editing work and + to Krishna Padmasola, Ben Orchard, and Mihai Maruseac for their transcription work. +

+
diff --git a/templates/reports/page.html b/templates/reports/page.html index bd12aa81..249e008e 100644 --- a/templates/reports/page.html +++ b/templates/reports/page.html @@ -2,8 +2,8 @@

$title$

-
- $author$, $date$ +
+ $author$, $date$
$if(summary)$

From 68c287f47d3996162efe73213d803ca9dcdd2589 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:38:30 +0200 Subject: [PATCH 4/8] Fix invalid markup and broken components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .divide-y-2 in main.css set a border width but no colour, so it fell back to currentColor and drew black 2px bars between project leaders. Dropped in favour of Tailwind's own divide utilities on the one element that used it. - Event tiles laid out dates/location in a whose
was right-aligned with no cell padding, so it rendered "Location:Rapperswil, Switzerland" with the label glued to the value. Now a two-column
. - Six templates wrapped already-rendered Markdown in

, producing

; browsers auto-closed it into stray empty paragraphs. - The nav and footer logos now carry width/height so they no longer reflow the page as they load. The remaining images come from template variables, whose intrinsic sizes the template cannot know. --- assets/css/main.css | 6 ------ templates/events/tile.html | 21 ++++++--------------- templates/footer.html | 2 +- templates/nav.html | 2 +- templates/projects/tile.html | 4 ++-- templates/resources/tile.html | 2 +- templates/who-we-are/exec-and-board.html | 8 ++------ templates/who-we-are/past-board.html | 12 +++--------- 8 files changed, 16 insertions(+), 41 deletions(-) diff --git a/assets/css/main.css b/assets/css/main.css index 56a80f23..25e61417 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -185,12 +185,6 @@ h6 { * { --tw-ring-inset: var(--tw-empty); } - -.divide-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(2px * (1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(2px * var(--tw-divide-y-reverse)); -} .youtube-video { position: relative; width: 100%; diff --git a/templates/events/tile.html b/templates/events/tile.html index 7bad13be..2c3f6271 100644 --- a/templates/events/tile.html +++ b/templates/events/tile.html @@ -3,21 +3,12 @@

$title$

$summary$

- - - - - - - - - - -
Dates: -

$daterange$

-
Location: -

$location$

-
+
+
Dates:
+
$daterange$
+
Location:
+
$location$
+
diff --git a/templates/footer.html b/templates/footer.html index b5263c34..695fbfbd 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -52,7 +52,7 @@
- Logo of the Haskell Foundation + Logo of the Haskell Foundation
diff --git a/templates/nav.html b/templates/nav.html index 64b4ab33..8ec9c524 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -2,7 +2,7 @@ diff --git a/templates/projects/tile.html b/templates/projects/tile.html index 5c7a14a9..7d68b84b 100644 --- a/templates/projects/tile.html +++ b/templates/projects/tile.html @@ -1,13 +1,13 @@

$title$

-

$body$

+ $body$
$subtitle$
-
+
$if(leader0name)$
diff --git a/templates/resources/tile.html b/templates/resources/tile.html index c80ed06b..faf19bb3 100644 --- a/templates/resources/tile.html +++ b/templates/resources/tile.html @@ -1,6 +1,6 @@

$title$

-

$body$

+ $body$
>> $link-text$ diff --git a/templates/who-we-are/exec-and-board.html b/templates/who-we-are/exec-and-board.html index 66a36dee..f0fd1866 100644 --- a/templates/who-we-are/exec-and-board.html +++ b/templates/who-we-are/exec-and-board.html @@ -28,9 +28,7 @@

$name$

$title$
-

- $body$ -

+ $body$ @@ -69,9 +67,7 @@

$name$

$endif$
-

- $body$ -

+ $body$ $if(false)$
Committees
diff --git a/templates/who-we-are/past-board.html b/templates/who-we-are/past-board.html index 696d849b..4026004f 100644 --- a/templates/who-we-are/past-board.html +++ b/templates/who-we-are/past-board.html @@ -30,9 +30,7 @@

$name$

$endif$
-

- $body$ -

+ $body$ $if(committees)$
Committees
@@ -79,9 +77,7 @@

$name$

$endif$
-

- $body$ -

+ $body$
$if(email)$ >> $email$ @@ -117,9 +113,7 @@

$name$

$interimBoardTitle$
-

- $body$ -

+ $body$
$endfor$
From dd047e4b6b65d14053baff3dbe9bde3660b6f64c Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:38:44 +0200 Subject: [PATCH 5/8] Remove dead and conflicting utility classes None of these did anything, and they made the templates hard to read: - "shadow-lg shadow-xl shadow-md shadow-sm" in 13 templates (16 occurrences): only the last one applies. Kept shadow-sm, the one that was in effect. - "border-gray-320" (19 occurrences in the mobile flyout) and "text-md" (2) are not Tailwind classes at all, so those borders never rendered. - "py-16 md:py-24 py-20 md:py-28 xl:py-32" on the homepage: two competing sets; kept the one that won at every breakpoint (measured: 80/112/128px). - "sm:px-12 md:px-12" (24 occurrences): the same value twice. --- templates/affiliates/list.html | 4 +-- templates/careers/list.html | 4 +-- templates/careers/page.html | 2 +- templates/donations/list.html | 4 +-- templates/events/page.html | 2 +- templates/footer.html | 2 +- templates/homepage.html | 8 ++--- templates/mobile-nav-flyout.html | 38 ++++++++++++------------ templates/news/page.html | 2 +- templates/partnerships/page.html | 2 +- templates/podcast/list.html | 2 +- templates/press/list.html | 2 +- templates/projects/list.html | 2 +- templates/reports/page.html | 2 +- templates/who-we-are/exec-and-board.html | 8 ++--- templates/who-we-are/past-board.html | 16 +++++----- 16 files changed, 50 insertions(+), 50 deletions(-) diff --git a/templates/affiliates/list.html b/templates/affiliates/list.html index 8382c387..9b860c3b 100644 --- a/templates/affiliates/list.html +++ b/templates/affiliates/list.html @@ -2,7 +2,7 @@ title: Affiliates ---
-
+
We invite existing Haskell projects, committees and communities to explicitly affiliate and align themselves @@ -19,7 +19,7 @@
-
+
diff --git a/templates/careers/list.html b/templates/careers/list.html index 645c1ac6..12c142bf 100644 --- a/templates/careers/list.html +++ b/templates/careers/list.html @@ -9,7 +9,7 @@

Careers

$if(openreqs)$
-
+

Working at the Haskell Foundation

Interested in working at the crossroads of the Haskell ecosystem? @@ -27,7 +27,7 @@

Working at $endif$
-
+

Haskell Foundation Sponsors Hiring Haskellers

Many of our sponsors are actively hiring Haskell engineers. diff --git a/templates/careers/page.html b/templates/careers/page.html index 3a3efa11..7cf03b66 100644 --- a/templates/careers/page.html +++ b/templates/careers/page.html @@ -1,6 +1,6 @@

-
+

$title$

$summary$ diff --git a/templates/donations/list.html b/templates/donations/list.html index c4a2be27..c08bb41a 100644 --- a/templates/donations/list.html +++ b/templates/donations/list.html @@ -121,7 +121,7 @@

Gold

-
+

In-Kind Support

@@ -130,7 +130,7 @@

In-Kind Su

-
+
diff --git a/templates/events/page.html b/templates/events/page.html index 4b447252..e5a90e3f 100644 --- a/templates/events/page.html +++ b/templates/events/page.html @@ -1,6 +1,6 @@
-
+

$title$

$summary$ diff --git a/templates/footer.html b/templates/footer.html index 695fbfbd..eb69c3c0 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -1,4 +1,4 @@ -

+
SPONSORS
diff --git a/templates/homepage.html b/templates/homepage.html index 01154ddf..ae2cf845 100644 --- a/templates/homepage.html +++ b/templates/homepage.html @@ -15,7 +15,7 @@

Amplify Haskell’s impact on humanity.

-
+
$if(openreqs)$ $for(openreqs)$ @@ -78,7 +78,7 @@

We drive change.

-
+

Not “just another programming language”

Haskell embodies a radical and elegant attack on the entire enterprise of writing software. It profoundly influences the world of software for the better. @@ -99,7 +99,7 @@

Our Ethos

-
+
@@ -145,7 +145,7 @@

Follow HF Developments

-
+

Haskell Foundation Discourse

diff --git a/templates/mobile-nav-flyout.html b/templates/mobile-nav-flyout.html index 9d63aace..c6d90f5a 100644 --- a/templates/mobile-nav-flyout.html +++ b/templates/mobile-nav-flyout.html @@ -20,31 +20,31 @@
- Home + Home -
About
- Current Board - Past Boards - Contact - Vision - Careers +
About
+ Current Board + Past Boards + Contact + Vision + Careers - Podcast + Podcast - Projects + Projects - Sponsorship + Sponsorship -
Affiliates
- Affiliates - About Affiliating +
Affiliates
+ Affiliates + About Affiliating -
News and Info
- News - Events - Press - Resources - FAQ +
News and Info
+ News + Events + Press + Resources + FAQ
diff --git a/templates/news/page.html b/templates/news/page.html index e26c03ec..be8b19dd 100644 --- a/templates/news/page.html +++ b/templates/news/page.html @@ -1,6 +1,6 @@
-
+

$title$

$if(author)$$author$, $endif$$date$ diff --git a/templates/partnerships/page.html b/templates/partnerships/page.html index 33723db2..9ccddb65 100644 --- a/templates/partnerships/page.html +++ b/templates/partnerships/page.html @@ -1,6 +1,6 @@
-
+

$title$

$summary$ diff --git a/templates/podcast/list.html b/templates/podcast/list.html index 94e3a967..a8765242 100644 --- a/templates/podcast/list.html +++ b/templates/podcast/list.html @@ -7,7 +7,7 @@

The Haskell Interlude

-
+

The Haskell Interlude is a Haskell-focused podcast where we diff --git a/templates/press/list.html b/templates/press/list.html index 7554c73e..b769b6d0 100644 --- a/templates/press/list.html +++ b/templates/press/list.html @@ -13,7 +13,7 @@

Press

-
+

Press Kit

diff --git a/templates/projects/list.html b/templates/projects/list.html index 84165679..1d208101 100644 --- a/templates/projects/list.html +++ b/templates/projects/list.html @@ -8,7 +8,7 @@

Projects

-
+

Volunteering

We are always looking for help on our projects. If you would like to volunteer, please reach out to us at volunteer@haskell.foundation, or join the Haskell diff --git a/templates/reports/page.html b/templates/reports/page.html index 249e008e..436361b2 100644 --- a/templates/reports/page.html +++ b/templates/reports/page.html @@ -1,6 +1,6 @@

-
+

$title$

$author$, $date$ diff --git a/templates/who-we-are/exec-and-board.html b/templates/who-we-are/exec-and-board.html index f0fd1866..e8b89df6 100644 --- a/templates/who-we-are/exec-and-board.html +++ b/templates/who-we-are/exec-and-board.html @@ -8,7 +8,7 @@

Who We Are

$if(currentexecutiveteam)$
-
+

Executive Team

@@ -17,7 +17,7 @@

Executive

-
+
$for(currentexecutiveteam)$
@@ -44,7 +44,7 @@

$name$

$endif$
-
+

The Board of Directors

@@ -54,7 +54,7 @@

The Board

-
+
$for(currentboard)$
diff --git a/templates/who-we-are/past-board.html b/templates/who-we-are/past-board.html index 4026004f..f91c89b7 100644 --- a/templates/who-we-are/past-board.html +++ b/templates/who-we-are/past-board.html @@ -6,7 +6,7 @@

Past Boards

-
+

Past Board Members

@@ -16,7 +16,7 @@

Past Board

-
+
$for(pastboard)$
@@ -26,7 +26,7 @@

Past Board

$name$

$if(title)$
$title$
-
$tenureStart$ - $tenureEnd$
+
$tenureStart$ - $tenureEnd$
$endif$
@@ -53,7 +53,7 @@

$name$

-
+

Past Executive Team Members

@@ -63,7 +63,7 @@

Past Execu

-
+
$for(pastexecutiveteam)$
@@ -73,7 +73,7 @@

Past Execu

$name$

$if(title)$
$title$
-
$tenureStart$ - $tenureEnd$
+
$tenureStart$ - $tenureEnd$
$endif$
@@ -92,7 +92,7 @@

$name$

-
+

The Interim Board of Directors

@@ -102,7 +102,7 @@

The Interi

-
+
$for(interimboard)$
From 32e541d340f14f0eb9b62dbe0759d6f146541128 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:39:08 +0200 Subject: [PATCH 6/8] Make links on dark surfaces readable, name icon links Links inside the bg-gray-800 panels and in the footer were purple-700 on near-black: 2.4:1, well under AA. They render light now (purple-200, ~10:1). This only became possible in this repo once main.css stopped overriding text utilities; see the earlier commit. The six footer social links were icon-only s with no text, so screen readers announced the URL. They get aria-labels, and the decorative is hidden. --- templates/affiliates/list.html | 2 +- templates/donations/list.html | 2 +- templates/footer.html | 15 ++++++++------- templates/podcast/list.html | 8 ++++---- templates/press/list.html | 2 +- templates/projects/list.html | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/templates/affiliates/list.html b/templates/affiliates/list.html index 9b860c3b..98ac176f 100644 --- a/templates/affiliates/list.html +++ b/templates/affiliates/list.html @@ -11,7 +11,7 @@
diff --git a/templates/donations/list.html b/templates/donations/list.html index c08bb41a..7098d3ed 100644 --- a/templates/donations/list.html +++ b/templates/donations/list.html @@ -125,7 +125,7 @@

Gold

In-Kind Support

- Non-financial contributions to HF are also welcome. As a volunteer-based organization, the Haskell Foundation needs volunteers who can work on software, documentation, promotion, and other tasks to support the HF affiliated projects and the Haskell community. Individuals who contribute their own time or on behalf of their company are the backbone of our organization. Please reach out to us at contact@haskell.foundation to learn more about how you can volunteer or offer in-kind support. + Non-financial contributions to HF are also welcome. As a volunteer-based organization, the Haskell Foundation needs volunteers who can work on software, documentation, promotion, and other tasks to support the HF affiliated projects and the Haskell community. Individuals who contribute their own time or on behalf of their company are the backbone of our organization. Please reach out to us at contact@haskell.foundation to learn more about how you can volunteer or offer in-kind support.

diff --git a/templates/footer.html b/templates/footer.html index eb69c3c0..5caab002 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -40,20 +40,21 @@
+
- - - - - - + + + + + +
Logo of the Haskell Foundation - +
diff --git a/templates/podcast/list.html b/templates/podcast/list.html index a8765242..da83792b 100644 --- a/templates/podcast/list.html +++ b/templates/podcast/list.html @@ -17,14 +17,14 @@

The Haskell Interlude

The music used is "Blue Lambda" by - Donya Quick. + Donya Quick. Many thanks to Donya for giving us permission to use this track for our podcast.

You can also follow the Interlude on - Mastodon, - Bluesky - and X. + Mastodon, + Bluesky + and X.

diff --git a/templates/press/list.html b/templates/press/list.html index b769b6d0..269a0c97 100644 --- a/templates/press/list.html +++ b/templates/press/list.html @@ -17,7 +17,7 @@

Press

Press Kit

- Please reach out to us at press@haskell.foundation for our + Please reach out to us at press@haskell.foundation for our press kit, including relevant marketing materials and branding.

diff --git a/templates/projects/list.html b/templates/projects/list.html index 1d208101..9f8363f6 100644 --- a/templates/projects/list.html +++ b/templates/projects/list.html @@ -11,7 +11,7 @@

Projects

Volunteering

- We are always looking for help on our projects. If you would like to volunteer, please reach out to us at volunteer@haskell.foundation, or join the Haskell + We are always looking for help on our projects. If you would like to volunteer, please reach out to us at volunteer@haskell.foundation, or join the Haskell Foundation Slack. If you have a proposal, we'd love to hear it!

From 3b47455f034f43d4964ff9ef1b2c3c5665d1e679 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 16:39:30 +0200 Subject: [PATCH 7/8] Regenerate the dev.css snapshot dev.css is the checked-in Tailwind build that Hakyll concatenates for contributors without a Node toolchain. The preceding commits introduce utilities it does not contain (divide-gray-200, grid-cols-[auto_1fr], w-fit, text-purple-200, hover:text-white, mt-12/md:mt-16), so local previews would have silently rendered without them. Produced with `npm run build:dev-snapshot` from tools/tailwind. --- dev.css | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/dev.css b/dev.css index c2e2bf83..c6baab9b 100644 --- a/dev.css +++ b/dev.css @@ -287,6 +287,24 @@ .z-20 { z-index: 20; } + .container { + width: 100%; + @media (width >= 40rem) { + max-width: 40rem; + } + @media (width >= 48rem) { + max-width: 48rem; + } + @media (width >= 64rem) { + max-width: 64rem; + } + @media (width >= 80rem) { + max-width: 80rem; + } + @media (width >= 96rem) { + max-width: 96rem; + } + } .m-4 { margin: calc(var(--spacing) * 4); } @@ -843,6 +861,13 @@ .w-48 { width: calc(var(--spacing) * 48); } + .w-auto { + width: auto; + } + .w-fit { + width: -moz-fit-content; + width: fit-content; + } .w-full { width: 100%; } @@ -876,6 +901,9 @@ .cursor-pointer { cursor: pointer; } + .grid-cols-\[auto_1fr\] { + grid-template-columns: auto 1fr; + } .flex-col { flex-direction: column; } @@ -913,6 +941,13 @@ margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse))); } } + .space-y-2 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); + } + } .space-y-4 { :where(& > :not(:last-child)) { --tw-space-y-reverse: 0; @@ -934,6 +969,10 @@ margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse))); } } + .gap-x-3 { + -moz-column-gap: calc(var(--spacing) * 3); + column-gap: calc(var(--spacing) * 3); + } .space-x-4 { :where(& > :not(:last-child)) { --tw-space-x-reverse: 0; @@ -955,6 +994,9 @@ margin-inline-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse))); } } + .gap-y-1 { + row-gap: calc(var(--spacing) * 1); + } .divide-y-2 { :where(& > :not(:last-child)) { --tw-divide-y-reverse: 0; @@ -964,6 +1006,11 @@ border-bottom-width: calc(2px * calc(1 - var(--tw-divide-y-reverse))); } } + .divide-gray-200 { + :where(& > :not(:last-child)) { + border-color: var(--color-gray-200); + } + } .self-start { align-self: flex-start; } @@ -1149,6 +1196,10 @@ font-size: var(--text-4xl); line-height: var(--tw-leading, var(--text-4xl--line-height)); } + .text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } .text-lg { font-size: var(--text-lg); line-height: var(--tw-leading, var(--text-lg--line-height)); @@ -1207,6 +1258,9 @@ .text-gray-600 { color: var(--color-gray-600); } + .text-purple-200 { + color: var(--color-purple-200); + } .text-purple-700 { color: var(--color-purple-700); } @@ -1249,6 +1303,13 @@ } } } + .hover\:text-white { + &:hover { + @media (hover: hover) { + color: var(--color-white); + } + } + } .focus\:outline-none { &:focus { --tw-outline-style: none; @@ -1598,6 +1659,11 @@ margin-top: calc(var(--spacing) * 12); } } + .md\:mt-16 { + @media (width >= 48rem) { + margin-top: calc(var(--spacing) * 16); + } + } .md\:mt-24 { @media (width >= 48rem) { margin-top: calc(var(--spacing) * 24); @@ -1725,6 +1791,12 @@ line-height: var(--tw-leading, var(--text-5xl--line-height)); } } + .md\:text-lg { + @media (width >= 48rem) { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + } .md\:leading-tight { @media (width >= 48rem) { --tw-leading: var(--leading-tight); From 9a48ecba04fd76c7e1ddb37949dc6dd8fc6cf816 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Sun, 26 Jul 2026 21:07:37 +0200 Subject: [PATCH 8/8] Serve one checked-in CSS file instead of dev.css plus a CI build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev.css was a preview-only snapshot concatenated onto the Tailwind entry point (whose @import/@theme the browser discards), while CI built the real stylesheet separately at deploy time. Two artifacts, both writing _site/assets/css/tailwind.css, and Hakyll clobbered the npm build on every rebuild. Now assets/css/tailwind.built.css is the only artifact: checked in, unminified, constRoute'd to assets/css/tailwind.css, and deployed as-is. The watchers chain rather than race — postcss writes the built file, Hakyll copies it — so `site watch` plus `npm run watch` gives live reload with real CSS, no env var needed. Dropping cssnano costs 837 gzipped bytes and buys a file that can be diffed and merged. CI no longer builds CSS for deploy, so its drift check is now what keeps a stale stylesheet off the live site. --- .github/workflows/main.yml | 27 +- CONTRIBUTING.md | 33 +- dev.css => assets/css/tailwind.built.css | 0 assets/css/tailwind.css | 31 +- site.hs | 37 +- tools/screenshot/shoot.py | 31 +- tools/tailwind/README.md | 30 +- tools/tailwind/package-lock.json | 937 ++--------------------- tools/tailwind/package.json | 6 +- tools/tailwind/postcss.config.js | 7 +- 10 files changed, 161 insertions(+), 978 deletions(-) rename dev.css => assets/css/tailwind.built.css (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bf4d9418..bbb9b91f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -84,23 +84,18 @@ jobs: - name: Build site run: stack exec --system-ghc site rebuild - - name: Build CSS - # The Node/Tailwind toolchain lives in tools/tailwind/; its npm scripts - # cd back to the repo root before invoking postcss. - working-directory: tools/tailwind - run: | - npm ci - npm run build:production - - # Gate every push/PR: dev.css is a checked-in snapshot of `npm run build` - # (concatenated onto tailwind.css by site.hs for Node-less previews). - # Regenerate it and fail if the committed copy has drifted. - - name: Check dev.css is in sync with Tailwind output + # assets/css/tailwind.built.css is the checked-in Tailwind output, which + # `site rebuild` above copied into _site — i.e. it is what ships. So this + # gate is what keeps a stale stylesheet from being deployed: rebuild it + # from source (the Node/Tailwind toolchain lives in tools/tailwind/, whose + # npm scripts cd back to the repo root before invoking postcss) and fail + # if the committed copy differs. + - name: Check assets/css/tailwind.built.css is in sync with Tailwind output run: | - npm --prefix tools/tailwind run build:dev-snapshot - if ! git diff --quiet -- dev.css; then - echo "::error file=dev.css::dev.css is out of date. Regenerate it with 'cd tools/tailwind && npm run build:dev-snapshot' and commit the result." - git --no-pager diff -- dev.css + (cd tools/tailwind && npm ci && npm run build) + if ! git diff --quiet -- assets/css/tailwind.built.css; then + echo "::error file=assets/css/tailwind.built.css::The checked-in CSS is out of date. Regenerate it with 'cd tools/tailwind && npm run build' and commit the result." + git --no-pager diff -- assets/css/tailwind.built.css exit 1 fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca9a1874..f6923f72 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,36 +76,39 @@ The site will be built in the `_site` directory, and you can open the files in y The site is styled with [Tailwind CSS](https://tailwindcss.com) (v4). Tailwind is a CSS framework whose classes are short abbreviations for inline styles (e.g. `pt-4`, `text-center`), scattered directly across the HTML templates and content. At build time the Tailwind compiler acts as a kind of "CSS tree-shaker": it scans the site for the Tailwind classes actually in use and emits only the CSS needed for them. -Two files matter: +Three files matter: -- `assets/css/tailwind.css` — the Tailwind entry point. It holds the v4 CSS-native configuration (`@theme` colors/fonts, `@plugin`, and the `@source` lines that tell the compiler which directories to scan). **New pages that use Tailwind classes must add an `@source` line here**, or those classes will be dropped from the production build. +- `assets/css/tailwind.css` — the Tailwind entry point. It holds the v4 CSS-native configuration (`@theme` colors/fonts, `@plugin`, and the `@source` lines that tell the compiler which directories to scan). **New pages that use Tailwind classes must add an `@source` line here**, or those classes will be dropped from the build. This file is compiler *input*; it is never served. +- `assets/css/tailwind.built.css` — the compiler's output, **checked into the repository** and served as `assets/css/tailwind.css`. This is the CSS the live site loads, so it must be regenerated and committed whenever it changes (see below). - `assets/css/main.css` — hand-written CSS that is *not* processed by Tailwind. This is the place for ordinary, non-Tailwind styles. ### Do I need Node installed? -**No, if you are only editing content or Haskell code.** Hakyll concatenates a checked-in snapshot, `dev.css`, onto `assets/css/tailwind.css` at build time (see the `match "assets/css/tailwind.css"` rule in `site.hs`). `dev.css` is a pre-generated copy of the real Tailwind output, so `stack exec -- site build` on its own produces a site that looks close enough to preview — no Node toolchain required. This keeps the site accessible to contributors of all skill sets. +**No, if you are only editing content or Haskell code.** Hakyll copies the checked-in `assets/css/tailwind.built.css` into the site, so `stack exec -- site build` alone gives you the real stylesheet — no Node toolchain required. This keeps the site accessible to contributors of all skill sets. -**Yes, if you are changing the appearance.** Because `dev.css` is only a snapshot, it does *not* reflect edits to `assets/css/tailwind.css`, `assets/css/main.css`, `tools/tailwind/postcss.config.js`, or any *newly used* Tailwind class. To regenerate the real CSS you need [Node.js](https://nodejs.org). The Node/Tailwind toolchain lives in [`tools/tailwind/`](tools/tailwind/) (see its README), separate from the Hakyll project, so run npm from there: +**Yes, if you are changing the appearance** — that is, editing `assets/css/tailwind.css`, `assets/css/main.css`, `tools/tailwind/postcss.config.js`, or *using a Tailwind class the site did not use before* (Tailwind only emits the classes it finds, so a brand-new class has no CSS until the compiler runs). You will need [Node.js](https://nodejs.org). The toolchain lives in [`tools/tailwind/`](tools/tailwind/) (see its README), separate from the Hakyll project, so run npm from there: ```bash cd tools/tailwind -npm ci # once, to install the toolchain -npm run build # compile -> _site/assets/css/tailwind.css -npm run build:production # minified build (NODE_ENV=production) +npm ci # once, to install the toolchain +npm run build # compile -> assets/css/tailwind.built.css +npm run watch # same, then recompile on source/content changes ``` If you do not want to install `npm` globally and have `nix` available, drop into the Nix development shell which provides `npm` and a basic Haskell toolchain. -If your change alters the CSS, regenerate the checked-in `dev.css` snapshot and commit it: +**Commit the regenerated `assets/css/tailwind.built.css` with your change.** CI reruns `npm run build` on every push and PR and fails if the committed copy has drifted, so a stale stylesheet cannot reach the live site. + +### Live preview + +Run both watchers and the two chain into each other — postcss writes `assets/css/tailwind.built.css`, which Hakyll then copies into `_site` and reloads: ```bash -cd tools/tailwind -npm run build:dev-snapshot # recompile the dev.css snapshot in place +cabal run site -- watch & # or: stack exec -- site watch +cd tools/tailwind && npm run watch ``` -CI runs this same command and fails the build if the committed `dev.css` is out of date, so a stale snapshot cannot slip through review. - -> **Note:** `dev.css` is only an *unminified* snapshot of `npm run build`; it still differs from the minified `npm run build:production` output shipped to production, so verify appearance-critical changes against a real production build. +> **Note:** `npm run watch` is *additive* — it picks up newly used classes, but a class you removed keeps its CSS until the next one-shot `npm run build`. So finish with `npm run build` before committing, or CI's check will complain about the leftovers. ## CI @@ -117,8 +120,8 @@ The general steps are: 2. Install Haskell and Node.js 3. Restore the cached build artefacts 4. Build the `site` executable -5. Rebuild the site contents using the `site` executable -6. Build the production CSS (`cd tools/tailwind && npm ci && npm run build:production`) +5. Rebuild the site contents using the `site` executable (this copies the checked-in CSS into `_site`) +6. Recompile the CSS (`cd tools/tailwind && npm ci && npm run build`) and fail if the committed `assets/css/tailwind.built.css` has drifted 7. Check out the `main` branch 8. Copy the `_site` directory over the `main` branch contents 9. Commit and push the site contents to the `main` branch. diff --git a/dev.css b/assets/css/tailwind.built.css similarity index 100% rename from dev.css rename to assets/css/tailwind.built.css diff --git a/assets/css/tailwind.css b/assets/css/tailwind.css index cc131301..bc0a212a 100644 --- a/assets/css/tailwind.css +++ b/assets/css/tailwind.css @@ -1,21 +1,18 @@ /** - * This is the Tailwind entry point (source). It is NOT overwritten by the - * build — its compiled *output* lands at _site/assets/css/tailwind.css (see - * steps below). Only put Tailwind config/directives here; hand-written CSS - * belongs in assets/css/main.css. + * This is the Tailwind entry point (source), and it is never served: only put + * Tailwind config/directives here, and hand-written CSS in assets/css/main.css. * - * NOTE: The underlying implementation details of how this works are as follows - * 1. tailwindcss needs these directives in order to know where to generate the - * output - * 2. first, hakyll is run and copies this source file verbatim to - * _site/assets/css/tailwind.css - * 3. then, after hakyll is done, postcss is run on _this_ source - * assets/css/tailwind.css file. Postcss will, during execution, invoke - * tailwindcss. tailwindcss (the compiler) will scan all the files in the - * site for tailwind classes (including _this_ source file). - * 4. postcss's output file is configured to be _site/assets/css/tailwind.css, - * so postcss overwrites the copy hakyll made in step 2 with the real, - * compiled CSS. This source file is never modified. + * How it reaches the browser: + * 1. postcss runs tailwindcss over this file (`npm run build` in + * tools/tailwind), scanning the @source paths below for utility classes, and + * writes the compiled CSS to assets/css/tailwind.built.css. That output is + * checked into the repo, so building the site needs no Node toolchain. + * 2. hakyll copies assets/css/tailwind.built.css to + * _site/assets/css/tailwind.css — the URL the templates link. This source + * file is neither modified nor copied. + * + * So: regenerate and commit the built file whenever you touch this one, use a + * new utility class, or edit the postcss config. CI fails on any drift. */ /* @@ -24,7 +21,7 @@ * @source paths below are scanned — mirrors the old * topLevelFoldersWithTailwind/topLevelFilesWithTailwind purge.content list. * New pages using Tailwind utility classes must add an @source line here, - * or their classes will be missing from the production build. + * or their classes will be missing from the compiled CSS. */ /* * The Node/Tailwind toolchain lives in tools/tailwind/ (see its README), so diff --git a/site.hs b/site.hs index 47de7269..99ffe664 100644 --- a/site.hs +++ b/site.hs @@ -40,32 +40,31 @@ config = defaultConfiguration ignoreFile defaultConfiguration file } +-- | The Tailwind entry point and its checked-in build output. Neither is served +-- under its own name: the entry point (@import@/@\@theme@/@\@source@) is +-- compiler input a browser cannot use, and the build output is routed to the +-- entry point's URL instead. +tailwindFiles :: Pattern +tailwindFiles = tailwindEntryPoint .||. tailwindBuilt + +tailwindEntryPoint, tailwindBuilt :: Pattern +tailwindEntryPoint = "assets/css/tailwind.css" +tailwindBuilt = "assets/css/tailwind.built.css" + -------------------------------------------------------------------------------------------------------- -- MAIN GENERATION ------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------- main :: IO () main = hakyllWith config $ do -- statics --------------------------------------------------------------------------------------------- - match "dev.css" $ compile getResourceString - match "assets/css/tailwind.css" $ do - route idRoute - -- We concatenate a dev.css file that exists at the root of the repository so that people don't - -- need to have nodejs setup or working in order to get a functional development experience - -- "why yes this is very crimes why do you ask" - -- dev.css is a checked-in snapshot of the Tailwind build; regenerate it - -- with `npm run build:dev-snapshot` from tools/tailwind and commit. CI - -- fails if it drifts. - compile $ do - devCss <- loadBody "dev.css" - fmap ((devCss ++ "\n") ++) <$> getResourceString - - -- Here is where interop with JS would happen if we wanted every - -- Haskell developer working on this site to also set up a Node toolchain - -- match "assets/css/*.css" $ do - -- route idRoute - -- undefined -- (insert some invoke "npm run build" step here) + -- The Tailwind build output is checked in, so a Node toolchain is needed to + -- *change* the CSS but not to build the site with the real thing. Regenerate + -- it with `npm run build` from tools/tailwind; CI fails if it has drifted. + match tailwindBuilt $ do + route $ constRoute "assets/css/tailwind.css" + compile copyFileCompiler - match "assets/**" $ do + match ("assets/**" .&&. complement tailwindFiles) $ do route idRoute compile copyFileCompiler diff --git a/tools/screenshot/shoot.py b/tools/screenshot/shoot.py index dd7c5fdd..a491ee97 100755 --- a/tools/screenshot/shoot.py +++ b/tools/screenshot/shoot.py @@ -4,24 +4,27 @@ Needs the Nix dev shell, which provides python3 with playwright and the browsers (`nix develop`, or automatically via direnv). -Full loop — generate the site, build the real CSS over it, serve, shoot: +Start the two watchers once, then shoot as often as you like — postcss keeps +`assets/css/tailwind.built.css` current and Hakyll copies it into `_site`: - cabal run site -- build && (cd tools/tailwind && npm run build) - cabal run site -- server & # http://127.0.0.1:8000 + cabal run site -- watch & # http://127.0.0.1:8000 + (cd tools/tailwind && npm run watch) & ./tools/screenshot/shoot.py /tmp/shots --slice 1100 home=/ news=/news/ ./tools/screenshot/shoot.py /tmp/shots --width 390 --slice 900 mhome=/ -After editing templates or CSS, repeat the first line — `site server` only -serves `_site`, it does not rebuild, so it will happily keep serving stale -pages. There is no need to restart it. - -Serve with `server`, not `watch`. `watch` recompiles on change, and Hakyll's -rule for `assets/css/tailwind.css` concatenates the checked-in `dev.css` -snapshot (so that contributors without Node get a styled site), which -overwrites what `npm run build` just wrote into `_site`. Since `dev.css` is a -*snapshot*, it lacks any utility class you have only just used in a template, -so the page silently renders without those classes. See -`.ai/todo/2026-07-26-unify-dev-css-preview.md` for fixing this properly. +For a one-shot look, build by hand and serve without recompiling: + + (cd tools/tailwind && npm run build) && cabal run site -- build + cabal run site -- server & + ./tools/screenshot/shoot.py /tmp/shots --slice 1100 home=/ + +Here you must repeat the first line after every template or CSS edit: `server` +only serves `_site`, it does not rebuild, so it will happily keep serving stale +pages (no need to restart it, though). + +Either way the CSS is the real thing — but `npm run watch` only ever *adds* +utilities, so a class you deleted still has styles until the next one-shot +`npm run build`. If a removal seems not to take effect, that is why. Targets are `name=path` (resolved against --base) or `name=full-url`: diff --git a/tools/tailwind/README.md b/tools/tailwind/README.md index 9a6c0d11..f97522e2 100644 --- a/tools/tailwind/README.md +++ b/tools/tailwind/README.md @@ -6,10 +6,12 @@ don't get tangled (`node_modules/` no longer sits next to the Haskell source, and Hakyll's `ignoreFile` just skips all of `tools/`). It contains only *build config* — `package.json`, `package-lock.json`, and -`postcss.config.js`. The CSS **sources** stay where Hakyll routes them from: +`postcss.config.js`. The CSS itself stays where Hakyll routes it from: - `assets/css/tailwind.css` — Tailwind v4 entry point (`@theme`, `@source`, `@plugin`). New pages using Tailwind classes must add an `@source` line here. +- `assets/css/tailwind.built.css` — this toolchain's output, checked in and + served as `assets/css/tailwind.css`. - `assets/css/main.css` — hand-written, non-Tailwind CSS. ## Usage @@ -17,12 +19,16 @@ It contains only *build config* — `package.json`, `package-lock.json`, and Run npm **from this directory**: ```bash -npm ci # install the toolchain (once) -npm run build # dev build -> ../../_site/assets/css/tailwind.css -npm run build:production # minified -> ../../_site/assets/css/tailwind.css (NODE_ENV=production) -npm run build:dev-snapshot # regenerate -> ../../dev.css (the Node-less preview snapshot) +npm ci # install the toolchain (once) +npm run build # compile -> ../../assets/css/tailwind.built.css +npm run watch # same, then recompile on source/content changes ``` +Pair `npm run watch` with ` site -- watch`: postcss +writes the built CSS, Hakyll copies it into `_site` and reloads. Note that +`--watch` only ever *adds* utilities, so finish with a one-shot `npm run build` +before committing (see [../../CONTRIBUTING.md](../../CONTRIBUTING.md)). + ## Why the scripts `cd ../..` Two Tailwind v4 constraints force the build to run from the **repo root**, not @@ -42,9 +48,13 @@ from here: The `--config tools/tailwind` flag points postcss back here for `postcss.config.js` after the `cd`. -## dev.css +## Why the output is checked in + +`assets/css/tailwind.built.css` is a generated file in version control, which is +a deliberate trade: contributors without a Node toolchain can build and preview +the site with the real CSS, and nothing has to run npm at deploy time. The +price is that it *is* the deployed stylesheet, so it must not go stale — CI +recompiles it on every push and PR and fails on any difference. -`dev.css` (at the repo root) is a checked-in snapshot of `npm run build` that -`site.hs` concatenates onto `tailwind.css` so contributors without Node can -still preview the site. CI fails if it drifts; regenerate it with -`npm run build:dev-snapshot` and commit. +There is no minified variant: cssnano saved ~800 bytes gzipped, which is not +worth a single-line file that cannot be diffed or merged. diff --git a/tools/tailwind/package-lock.json b/tools/tailwind/package-lock.json index 4a3733b4..ce98fbe3 100644 --- a/tools/tailwind/package-lock.json +++ b/tools/tailwind/package-lock.json @@ -13,7 +13,6 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.19", "autoprefixer": "^10.5.0", - "cssnano": "^8.0.0", "postcss": "^8.5.14", "postcss-cli": "^11.0.1", "tailwindcss": "4.2.4" @@ -31,13 +30,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@colordx/core": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.4.3.tgz", - "integrity": "sha512-kIxYSfA5T8HXjav55UaaH/o/cKivF6jCCGIb8eqtcsfI46wsvlSiT8jMDyrl779qLec3c2c2oHBZo4oAhvbjrQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -306,6 +298,64 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.8.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.8.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", @@ -467,13 +517,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -521,19 +564,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001792", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", @@ -615,60 +645,6 @@ "dev": true, "license": "MIT" }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -682,120 +658,6 @@ "node": ">=4" } }, - "node_modules/cssnano": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-8.0.0.tgz", - "integrity": "sha512-FBjMD2UTnAshdTNoFnF6vLICR3/gCypkWdveXbxpO+cPlJZ3RYdYOv84ibCxEHzo5Wuoy7yAmBkmUGuLLzoUwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssnano-preset-default": "^8.0.0", - "lilconfig": "^3.1.3" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/cssnano-preset-default": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-8.0.0.tgz", - "integrity": "sha512-uIJx5WOQ2vlH0HwKZz3AbhYffVvYTUcS2S76gr3oLnU2i9qn7yy3LLs0rk3YAtBB9gayYr0aTvk0pZ6MlenUlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "cssnano-utils": "^6.0.0", - "postcss-calc": "^10.1.1", - "postcss-colormin": "^8.0.0", - "postcss-convert-values": "^8.0.0", - "postcss-discard-comments": "^8.0.0", - "postcss-discard-duplicates": "^8.0.0", - "postcss-discard-empty": "^8.0.0", - "postcss-discard-overridden": "^8.0.0", - "postcss-merge-longhand": "^8.0.0", - "postcss-merge-rules": "^8.0.0", - "postcss-minify-font-values": "^8.0.0", - "postcss-minify-gradients": "^8.0.0", - "postcss-minify-params": "^8.0.0", - "postcss-minify-selectors": "^8.0.0", - "postcss-normalize-charset": "^8.0.0", - "postcss-normalize-display-values": "^8.0.0", - "postcss-normalize-positions": "^8.0.0", - "postcss-normalize-repeat-style": "^8.0.0", - "postcss-normalize-string": "^8.0.0", - "postcss-normalize-timing-functions": "^8.0.0", - "postcss-normalize-unicode": "^8.0.0", - "postcss-normalize-url": "^8.0.0", - "postcss-normalize-whitespace": "^8.0.0", - "postcss-ordered-values": "^8.0.0", - "postcss-reduce-initial": "^8.0.0", - "postcss-reduce-transforms": "^8.0.0", - "postcss-svgo": "^8.0.0", - "postcss-unique-selectors": "^8.0.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/cssnano-utils": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-6.0.0.tgz", - "integrity": "sha512-ztS9W/+uaDn+bkYmDhs+GdMveHJ3CL8IPNHpRqDUQXv5GJOTQAJjV1XUOInr9esLXSabQV1pLRZlJpyUwEqDyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/dependency-graph": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", @@ -815,65 +677,6 @@ "node": ">=8" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.351", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.351.tgz", @@ -901,19 +704,6 @@ "node": ">=10.13.0" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1362,20 +1152,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true, - "license": "MIT" - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1385,13 +1161,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -1427,19 +1196,6 @@ "node": ">=0.10.0" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1497,37 +1253,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-calc": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", - "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12 || ^20.9 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.38" - } - }, - "node_modules/postcss-calc/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-cli": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz", @@ -1557,111 +1282,6 @@ "postcss": "^8.0.0" } }, - "node_modules/postcss-colormin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-8.0.0.tgz", - "integrity": "sha512-KKwMmsSgsmdYXqrjQeqL3tnuIFtctiR1GEMHdjNpDpz/TCRkkkok2mMcreK2zVV3l7POWOmAkR2xYHUpRUK1DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@colordx/core": "^5.4.3", - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-convert-values": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-8.0.0.tgz", - "integrity": "sha512-Ohtj3rNZWawTRePv5NCHTy8VJSdJ/G/uKuxcxJreOMichuqcT6uEl2TAnopVeJCJ/c13jaSqg7m63yFLM5zBsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-discard-comments": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-8.0.0.tgz", - "integrity": "sha512-zGpvVLj2sbagEp+BTVETvAfkZdGVA6rALNujDK/WTIjdf1/rQOxOG8BBzkI8UQgnw8SkL6xffAfbtGMHFypadw==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-discard-comments/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-8.0.0.tgz", - "integrity": "sha512-zjRyYmNGI3PTipKBBtCgExlmZXQn49KvKoaiNnR2g+iXxeNk7GY5Js2ULtZXPrCYeqjPagrzKIBNcBocvXCR7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-discard-empty": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-8.0.0.tgz", - "integrity": "sha512-kxPJg6EqahbBvm+l7hpYYCtpsv8dlz7Tv6wJXUXZaeuY0WGS61DxfGdZR4uVB/Cx+yi3iOHQVSqpSHKMFaBg6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-8.0.0.tgz", - "integrity": "sha512-sW2OWH3l9p0FmBSVr228uztFseqroZxwgD7SGF0Ks0dRPDttSo3P8FK5ZBLtWBH2A5+chpB0J2fB/T8heKHLBw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, "node_modules/postcss-load-config": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz", @@ -1702,333 +1322,6 @@ } } }, - "node_modules/postcss-merge-longhand": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-8.0.0.tgz", - "integrity": "sha512-YDmAmQ8H+ljfomVpSXvr9NA0GP01fraQJqjWBYoMVGg6rOT+PJLwPyeVo2ekn4WB4ZVSH5ddtK3DTRxbz6CFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^8.0.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-merge-rules": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-8.0.0.tgz", - "integrity": "sha512-bgstL5mpi41dDpnYGDUcI3M814NWkCMcIWpwDqEHXkHg3BT7b4XRAfNEuwJncZOVn/67kVKvWzhfv/7xyrp2uQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^6.0.0", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-8.0.0.tgz", - "integrity": "sha512-EnOHQEnSt6oH5NrL1DMFAQuwB2IOimFXTCzc9bKfUeH1jREbqIF5MAK4gQJQOC4mPUwJt4sWifAmNZ1qLu6j3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-8.0.0.tgz", - "integrity": "sha512-43iAnYIGk0ZjNx5X/rkIcHi6dhmu/vEjY0kqfUfxPuJRO+V7jx8uKIdcnL0dpfNoC5J9TSh3EtzLWbq0gpqnWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@colordx/core": "^5.4.3", - "cssnano-utils": "^6.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-minify-params": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-8.0.0.tgz", - "integrity": "sha512-z7w4QO7G55l4vMUK1Lmx03GW7iyRLgf2V5Dz/7ioSPLnXRjeD+b7m0XfAXUGrbBYYrJ6bXPk+3LoX5u4JfAcSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "cssnano-utils": "^6.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-8.0.0.tgz", - "integrity": "sha512-pQKzVGjUfOVP8QH95lXN/cpMcwHvdsqF3tQTEWPsRPnHw2SZ7G9c69oWDfjWkpCd81APF8E34QKUEUo+qUp5qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-api": "^3.0.0", - "cssesc": "^3.0.0", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-8.0.0.tgz", - "integrity": "sha512-s88FUNDSUD8m0wBYvTQQcubVts6zhXwBU8zCD4vkRKiecd0v8cOjHVIF9r/i+5xzS/WG3f98qq4XsOM0JqvfLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-8.0.0.tgz", - "integrity": "sha512-gG2nBxD27fiw6Luinb1QYKdM/Co5GornRJgSD+JTwNH4PGKxImP0qyruDDav49aHUPLY3qrL3qN3LvybO7IzxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-8.0.0.tgz", - "integrity": "sha512-t/wGqpehS20Ke7kc4QAsWpH+AJjUdMK/V5qV2RhrXkj8hO/fT1t1MJ8NL7sedWYk7ZqC7eISEJQonW5j0tU1MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-8.0.0.tgz", - "integrity": "sha512-3ebOmGdCYKrBYyGKc1xhj0unEnW7beZpVU7JohVeGl7mTxR+7T6egpaawTWAVsB0pEIhcsbJVOjPKCJSoRO6Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-string": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-8.0.0.tgz", - "integrity": "sha512-TvWCGZ/e04Tv31uJvOUtbexkfgUnqmQ3M2P5DkAaVzvOj+BvTkG2QjpA5Y71SL1SPxJcj4M23fNh+RDVCmG8kA==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-8.0.0.tgz", - "integrity": "sha512-uEfaXst5Xgqxv7geYUuz6vs9mn88K2NPY2RoIzM3BMmSjsdTSeppV9x2qIgrxsisdbSqF6IVhzI2occcte3hTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-8.0.0.tgz", - "integrity": "sha512-+WYngZaChEeTHZmWhmKtnJ4gTzWdINEaFcgWBnu6WdVu8Ftim8OBTcw768DuCC/3Aax9bZ9WkwrLGHym2Lzf+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-8.0.0.tgz", - "integrity": "sha512-4Mz9hZHn/QIB+YtFqTXrDmE2193GYxGb3F8uMfLvMicaEXCCUlDIJ658gFFJbqEGl9FYzwPtRiuNgbwlO9kkBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-8.0.0.tgz", - "integrity": "sha512-V1f8tYnwIP5tscOXQFTKK8Y5EJ+R2GMpFJ6FjzwoKoQnhbqQy3IeSrDjJJb8JjVos8ut6Osi80Zybpayv/XjIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-ordered-values": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-8.0.0.tgz", - "integrity": "sha512-Dg9+itb6lmD0bxqhQyHCtXAwYRh0wUrx6Mp4/BNXgkLoJmdYMmWi+V+Pypw79Q6iQhxA8KFMHqLBITQJV2gKMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssnano-utils": "^6.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-8.0.0.tgz", - "integrity": "sha512-DChcE9d528AKrlpCTHjhsAiOsWCk4H9ApHPS1QqRT3praObWTiWyn6W1UddGpc46K9LQnHwUu4YwaPUukGtXVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-8.0.0.tgz", - "integrity": "sha512-cLZT0som7vvumQT9XQCnSKOSnRinNQZd1Hm+J723Ney13E8CIydDhw6JwzsjPtgnYThTqn9Q45906gz6wxaAsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, "node_modules/postcss-reporter": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", @@ -2070,53 +1363,6 @@ "node": ">=4" } }, - "node_modules/postcss-svgo": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-8.0.0.tgz", - "integrity": "sha512-Q2fMSYEiNE1ioDc/3sxvI24NdgA/MJno2XLNpOxgv8aCcJbym8mZY10/lDY5+AWCIc3Aiqzy2Wcp9/zaIXBZgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^4.0.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-8.0.0.tgz", - "integrity": "sha512-iObuolUX+ITJfMU2QQFQdh31JgSjNLPNjVs6YGAqBHvOvAWXMMNget6donQl83aQaeS32i5XeKZURUW/WBxIUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/postcss-unique-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -2167,16 +1413,6 @@ "node": ">=0.10.0" } }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -2227,63 +1463,6 @@ "node": ">=8" } }, - "node_modules/stylehacks": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-8.0.0.tgz", - "integrity": "sha512-sWyjaJvBqHoVKYPbQ8JRvrGSPaYWtWrJsU+fGVtwKB1GE1rRPu3rC7T6UCuXLoL00Dwb+tsHe2T904r8Vnsx8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^22.11.0 || ^24.11.0 || >=26.0" - }, - "peerDependencies": { - "postcss": "^8.5.14" - } - }, - "node_modules/stylehacks/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/svgo": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", - "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.5.0" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, "node_modules/tailwindcss": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", diff --git a/tools/tailwind/package.json b/tools/tailwind/package.json index 4a2bd84f..08036cb7 100644 --- a/tools/tailwind/package.json +++ b/tools/tailwind/package.json @@ -4,14 +4,12 @@ "author": "Haskell Foundation", "private": true, "scripts": { - "build": "cd ../.. && postcss build assets/css/tailwind.css -o _site/assets/css/tailwind.css --config tools/tailwind", - "build:production": "cd ../.. && NODE_ENV=production postcss build assets/css/tailwind.css -o _site/assets/css/tailwind.css --config tools/tailwind", - "build:dev-snapshot": "cd ../.. && postcss build assets/css/tailwind.css -o dev.css --config tools/tailwind" + "build": "cd ../.. && postcss build assets/css/tailwind.css -o assets/css/tailwind.built.css --config tools/tailwind", + "watch": "cd ../.. && postcss build assets/css/tailwind.css -o assets/css/tailwind.built.css --config tools/tailwind --watch" }, "devDependencies": { "@tailwindcss/typography": "^0.5.19", "autoprefixer": "^10.5.0", - "cssnano": "^8.0.0", "postcss": "^8.5.14", "postcss-cli": "^11.0.1", "tailwindcss": "4.2.4" diff --git a/tools/tailwind/postcss.config.js b/tools/tailwind/postcss.config.js index 3ba613ea..d369a8e8 100644 --- a/tools/tailwind/postcss.config.js +++ b/tools/tailwind/postcss.config.js @@ -9,9 +9,8 @@ module.exports = { '@tailwindcss/postcss': {}, // handles browser compatibility 'autoprefixer': {}, - ...(process.env.NODE_ENV === "production" ? { - // Minify via cssnano, but only if NODE_ENV is set to production - cssnano: {}, - }: {}) + // No minifier: the output is checked in as assets/css/tailwind.built.css and + // shipped as-is, and a readable diff is worth more than the ~800 gzipped + // bytes cssnano saved. } }