A customized Tailwind stylesheet for First Draft projects — the successor to pico.firstdraft.css, per firstdraft/appdev#758. It provides classless styling for semantic HTML (the Pico teaching phase) plus Bootstrap-feel named components (.btn-primary, .card, .navbar, .alert, …) for the later phase, all built on Tailwind's default design tokens.
<!-- The official Tailwind CSS browser build (Preflight + utility classes, compiled on the fly) -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3"></script>
<!-- Our classless styles + named Tailwind components -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/firstdraft/tailwind.firstdraft.css@latest/tailwind.firstdraft.css">Then nothing applies until you add the gating class to <body>:
<body class="appdev-style">— the direct replacement for Pico's class="pico". Without it, pages get Tailwind's Preflight and utility classes only.
Instead of @latest you can pin a release, e.g. @0.0.1 (the version number from package.json).
Two source files in src/, one published artifact at the repo root:
src/tailwind.firstdraft.css— the handwritten stylesheet. Everything lives in thecomponentscascade layer, so utility classes students write always win over these defaults. Every value references a Tailwind theme variable (var(--color-zinc-200),calc(var(--spacing) * 4), …), never a literal, keeping the classless styles in lockstep with the utility classes.src/tailwind.theme.css— generated: Tailwind's full default theme as plain CSS custom properties. Required because the browser build tree-shakes its theme (it only emits variables that utility classes on the page actually use) and cannot load external files.tailwind.firstdraft.css(repo root) — the built artifact apps actually link: the source file with the theme baked in where its@importsits. One file, one request, variables available at first paint.
- Edit
src/tailwind.firstdraft.css(never the root artifact — it is overwritten). - Run
bin/buildand commit both files. - Bump the SemVer version in
package.json. Pushing that bump tomainruns therelease.ymlworkflow, which verifies the artifact is freshly built and creates the release tag — automatically making the new version available via jsDelivr.
The baked-in theme is generated from the @tailwindcss/cli version pinned in package.json. To move to a newer Tailwind:
- Update the pin in
package.json(devDependencies→@tailwindcss/cli). - Run
bin/build --regenerate-theme. - Update the
<script src=".../@tailwindcss/browser@X.Y">pin in app layouts to the same minor version.
Keeping the script tag and the baked theme on the same minor is what prevents drift: a newer browser build could emit utilities referencing theme variables the frozen artifact doesn't define yet.
- The gating class is named
appdev-style(notprose) so@tailwindcss/typographycould be added later without conflict. - Selectors are individually prefixed with
body.appdev-stylerather than wrapped in CSS nesting — nesting desugars selector lists into:is(), which takes the max specificity of the list and would break overrides like.btn-outlinevs the basebuttonrule. - Dark mode: the stylesheet's surfaces are tokenized, and a
[data-theme="dark"]block flips the palette when something stampsdata-theme="dark"on<html>(e.g. a Stimulus controller). Tailwind's owndark:variant keys offprefers-color-scheme; to make it follow the same toggle, a page must also declare@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));inside a<style type="text/tailwindcss">block. - Interactive components (modals, dropdowns, tabs, dismissible alerts) are styled here but need small Stimulus controllers for behavior; see the experiment PRs on #758.