Imported from DataIntegrationGroup/OcotilloMockups (
AGENTS.md). Install upstream withnpx skills add DataIntegrationGroup/OcotilloMockups. Copyright stays with the author.
AGENTS.md
Lo-fi HTML mockups for Ocotillo. They exist to get a design argued about before anyone builds it. Nothing here is production code and nothing here is wired to a backend.
The hard rule: one mockup is one file
Every mockup is a single self-contained .html file. All CSS and all
JavaScript are embedded in it.
No linked stylesheets, no <script src>, no CDN, no build step, no bundler, no
npm install, no framework. Someone must be able to double-click the file, or
receive it over email or Slack, and see the design — offline, on a machine with
no toolchain, five years from now.
Do not "improve" this by extracting shared files and linking them. That has been tried in this repo and reverted. If you find yourself wanting a shared asset, see Shared styles below — the sharing happens at author time, not at load time.
The one exception
The Google Fonts <link> in each file's <head>. Public Sans and Outfit are
the app's typefaces, and base64-embedding two variable fonts would add hundreds
of kilobytes to every mockup. The font stacks fall back to system-ui, so a
mockup opened offline is correct in every respect except the typeface.
That is the only external reference permitted. Everything else is inline.
Shared styles
The look-and-feel is shared, but it is shared by copying, not linking.
design-system/ocotillo.cssis the single source of truth. Edit it here.design-system/sync.pycopies it into every mockup, inside a delimited block.- The block in each mockup is generated. Never hand-edit it; your edit will be overwritten on the next sync.
python3 design-system/sync.py # write the block into every mockup
python3 design-system/sync.py --check # exit 1 if any mockup is stale
Each mockup therefore has two <style> blocks, in this order:
- The generated design-system block, between the
design-system:startanddesign-system:endcomment markers. - The mockup's own
<style>, holding only genuinely page-specific rules under a/* ── Page-specific ── */banner.
If a rule would be useful to a second mockup, it belongs in ocotillo.css. If
it is one-off, keep it local. When unsure, keep it local — promoting later is
easy, and un-inventing a token nobody wanted is not.
JavaScript goes in a single <script> block at the bottom of <body>. Plain
DOM, no libraries. Mockups only need enough behavior to demonstrate the flow —
switching tabs, toggling a checkbox, moving between steps.
Making a new mockup
cp design-system/template.html <area>-<feature>-mockup.html
The template already carries a synced design-system block, the app chrome, and a placeholder mockup note. Then:
- Set the title, breadcrumb, and active sidebar item.
- Write the mockup note first. See below.
- Build the page from classes in
design-system/components.html, which renders every available class. Open it before inventing anything. - Run
python3 design-system/sync.pyto be sure the block is current. - Verify (checklist below).
Name files <area>-<feature>-mockup.html, matching the existing
chemistry-*, field-planning-* pattern.
The mockup note
Every mockup opens with a .mockup-note. It is deliberately styled outside the
token system — yellow, obviously not product UI — so no reviewer mistakes
commentary for design.
It states two things:
- That the mockup is not functional.
- The open questions the mockup exists to force a decision on.
The second part is the point. A mockup with no open questions listed is either finished or not being honest. Write it before building the screen, not after.
Example deliverables (examples/)
Some mockups describe a screen that produces something — a PDF, an export, a
printed report. examples/ holds a rendered sample of that output, so a
reviewer can hold the artifact instead of imagining it.
These are not mockups, and the naming and mockup-note rules above do not apply. What does apply:
- The source
.htmlis still standalone — all CSS inline, no<script src>. Same rule, same reason. - The rendered PDF is committed alongside its source. It is the point of the folder; a reviewer should not need a toolchain to see it.
examples/render.shregenerates a PDF via headless Chrome. Re-run it after editing the source and commit both.- On-screen fit is not print fit. A page that measures correctly in the browser can still push its footer onto a blank sheet. Always render and count pages: a 3-page report that comes out 5 pages is the usual symptom.
- Anything scannable must be real. A placeholder QR is fine in a mockup and not fine on a sample deliverable — someone will scan it. Encode the actual URL and inline the result as a static path.
Mark the sample clearly as a sample, in a screen-only banner that does not print. The artifact should be unmistakable as an example when read on screen and clean when put on paper.
Design rules
design-system/README.md has the full set, including where each value comes
from in the app. The ones most often gotten wrong:
- Primary is
#0e6da8, the custom Ocotillo brand ramp — not a Tailwind blue. - The page background is
#fafafa. Cards are white and separated by a 1px ring, not by a shadow and not by a contrasting page color. - Tint with
color-mix, 10% fill and 30% border of a token. Never hand-picked hex pairs — they drift from the token and break dark mode. --primary-darkis the hover/emphasis slot, not "a darker primary". In dark mode it resolves lighter.--bloomand--sandare brand identity, never semantic. The bloom sits 8.7 degrees of hue from--destructiveand reads as an alarm in UI chrome.
Fake data should be plausible New Mexico groundwater data: real counties, real aquifer names, sane units and magnitudes. A reviewer distracted by a nonsense arsenic value is not reviewing the design.
Before committing
python3 design-system/sync.py --checkpasses.- The file opens correctly from
file://with no server running. This is the real test of the standalone rule. - No console errors.
- Every class used resolves, and every
var(--token)resolves. - It reads correctly in dark mode (
document.documentElement.classList.add('dark')). - The mockup note is present and lists real open questions.
There is a static server config in .claude/launch.json (python3 -m http.server 8931) if you want one for convenience, but a mockup that needs it
is broken.
Commits
Conventional commits, scoped by area — matching the existing history:
init(chemistry): chemistry report exporter mockup
feat(design-system): extract shared design system from OcotilloUI
refactor(chemistry): revert changes on field parameters table
Use init(...) for a brand-new mockup, feat(...) for a meaningful addition,
refactor(...)/fix(...) as usual. Commit only when asked.