A customized Tailwind stylesheet for your AppDev projects — the successor to pico.firstdraft.css. It provides classless styling for semantic HTML plus Bootstrap-feel named components (.btn-primary, .card, .navbar, .alert, ...), 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.appdev.css@latest/tailwind.appdev.css">Then nothing applies until you add the gating class to <body>:
<body class="appdev-styles">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).
Modals, dropdowns, tabs, dismissible alerts, and the dark-mode toggle are styled
here but need a little JavaScript to actually move. The Stimulus controllers
that supply it live in javascript/controllers/, with
the markup each one expects documented alongside.
They are copy-paste examples, not a dependency — copy the file you need into
app/javascript/controllers/ and importmap picks it up automatically. Keep the
filename: Stimulus derives data-controller="modal" from modal_controller.js.
Two source files in src/, one published artifact at the repo root:
src/tailwind.appdev.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.appdev.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.appdev.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-styles(notprose) so@tailwindcss/typographycould be added later without conflict. - Selectors are individually prefixed with
body.appdev-stylesrather 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>—dark_mode_controller.jsdoes that. 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. - The published artifact is only ever
tailwind.appdev.cssat the repo root.src/andjavascript/are reference material — jsDelivr will happily serve them, but nothing is expected to link them.