How this skill is triggered — by the user, by Claude, or both
Slash command
/co2-skills:mockgen-tailwindThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
---
Generate a Node.js + HTMX + Alpine.js mockup server from PRD.md for UI/UX designer review. Header, footer, and sidebar are served as partials. Content pages are HTMX fragments swapped into a shell layout. All image/PDF links open in new tabs.
| Layer | Technology |
|---|---|
| Server | Node.js + Express.js |
| Partial loading / navigation | HTMX 2.x |
| Interactive UI (dropdowns, dark mode) | Alpine.js 3.x |
| Styling | Tailwind CSS (CDN) |
| Icons | Inline Heroicons SVG |
This skill uses standardized input resolution. Provide:
| Argument | Required | Example | Description |
|---|---|---|---|
<application> | Yes | hub_middleware | Application name to locate the context folder |
<version> | Yes | v1.0.3 | Version to scope processing (filter user stories <= this version) |
module:<name> | No | module:Location Information | Limit generation to a single module |
The application name is matched against root-level application folders:
<number>_ prefix from folder names (e.g., 1_hub_middleware → hub_middleware)| File | Resolved Path |
|---|---|
| PRD.md | <app_folder>/context/PRD.md |
| Module Models | <app_folder>/context/model/ |
| Output (mockup) | <app_folder>/context/mockup/ |
/mockgen-tailwind hub_middleware v1.0.3 (all modules, up to v1.0.3)/mockgen-tailwind hub_middleware v1.0.3 module:Location Information (one module, specific version)/mockgen-tailwind "Hub Middleware" v1.0.3 module:Employer (title-case app name)module:Location Information), only generate/update screens
for that specific module. All other modules are skipped. Common screens (home, profile,
account, notifications), partials (shell, header, footer, sidebars), and server files are
NOT regenerated when a module filter is active — only the module's own content fragments
are written (and MOCKUP.html is updated for only that module's cards).Argument parsing: The module: prefix is the canonical form. Also accept:
module:"Location Information" (quoted, with space)module:location_information (snake_case — convert to title-case for matching)for Location Information module, only Location InformationBefore starting any work, resolve the application folder first (see Input Resolution below), then check CHANGELOG.md in the application folder (<app_folder>/CHANGELOG.md):
<app_folder>/CHANGELOG.md does not exist, skip this check (first-ever execution for this application).<app_folder>/CHANGELOG.md exists, scan all ## vX.Y.Z headings and determine the highest version using semantic versioning comparison."Version {requested} is lower than the current application version {highest} recorded in <app_folder>/CHANGELOG.md. Execution rejected." Do NOT proceed with any work.Read the auto-resolved PRD.md file and extract:
Application name: Derive from the parent folder name containing PRD.md.
Strip leading number and underscore prefix, then title-case.
Example: 1_hub_middleware -> "Hub Middleware"
Application initials: First letter of each word, uppercase.
Example: 1_hub_middleware -> "HM"
Modules: Each ## Module Name section under a # Module Category heading.
Record the module name and its description (the line after the heading).
User stories per module: Lines matching - [USxx#####] As a {Role} user, I want to...
Extract: tag, role, action summary.
Unique roles: Collect all distinct roles from user stories. Example: "Hub Administrator", "Hub Operation Support"
Target version (from input argument): If a version was provided, record it for filtering in the next sub-step.
PRD.md is a version-controlled document. Each section (User Story, Non Functional
Requirement, Constraint, Reference) has a version tag in square brackets, e.g., [v1.0.1].
Items may also be marked with strikethrough (~~) to indicate they are deprecated/removed.
Strikethrough exclusion (always applied, regardless of version parameter):
~~strikethrough~~ markup MUST be excluded from processing~~[USHM00006] As a Hub Administrator user, I want to...~~ → SKIPVersion filtering (applied only when a target version is provided):
[v1.0.0] or [v1.0.1]v1.0.1):
Version tracking per section: Record which version tag each item belongs to, as this will be used for traceability in the generated screens.
Example parsing of a section with multiple versions:
### User Story
[v1.0.0]
- ~~[USHM00006] As a Hub Administrator user, I want to manage...~~
- [USHM00009] As a Hub Administrator user, I want to map...
[v1.0.1]
- [USHM00012] As a Hub Administrator user, I want to manage the list...
With target version v1.0.0:
With target version v1.0.1 (or no version specified):
When a module argument is present, apply module filtering after version filtering:
location_information → match "Location Information").Module-filtered generation mode differs from full generation in these ways:
| Aspect | Full Generation | Module-Filtered |
|---|---|---|
| Common screens (home, profile, account, notifications) | Generate for every role | SKIP — already exist |
| Partials (shell, header, footer) | Generate | SKIP — already exist |
| Sidebar per role | Generate | SKIP — already exist |
| server.js + package.json | Generate | SKIP — already exist |
| Module content files (target module) | Generate | Generate / overwrite |
| Module content files (other modules) | Generate | SKIP — leave untouched |
| MOCKUP.html | Generate full file | Update only the target module's cards |
| footer.html version string | Update | Update (version may have changed) |
MOCKUP.html partial update (module-filtered mode):
After parsing PRD.md, look for module models at the auto-resolved model path:
<app_folder>/context/model/
For each module extracted in Step 1:
Convert the module name to kebab-case to derive the model folder name:
location-information, "Industrial Classification" → industrial-classification, "Employer" → employerCheck for {model_dir}/{kebab-module}/model.md
If the file exists, parse it and extract the following sections:
Store this as the module model for the module, keyed by module name
Field classification (used during content generation in Step 6e):
| Category | Definition | Usage |
|---|---|---|
| System fields | _id, _audit, _version, deleted, deletedAt, deletedBy | Exclude from user-facing forms |
| Audit-only fields | Fields whose Source is CONVENTION and type is Audit | Show in detail views only |
| Required form fields | Required: Yes AND not a system field | Mandatory inputs in create/edit forms |
| Optional form fields | Required: No AND not a system field | Optional inputs in create/edit forms |
| Read-only after creation | Fields marked as unique identity keys (e.g., companyRegistrationNumber) | Show in edit forms as readonly |
| Search/filter fields | Fields referenced in Index Recommendations | Render as filter controls in list screens |
| Enum fields | Type matches an entry in Section 7 Enum Definitions | Render as <select> dropdowns |
| Embedded object fields | Type is a custom embedded document type (not a primitive) | Render as <fieldset> sub-group |
| Embedded array fields | Type ends in [] (e.g., PersonInCharge[]) | Render as repeatable row with Add/Remove |
Fallback: If no model.md exists for a module, infer fields from user story text (original behavior).
Load the design system using a two-tier resolution strategy:
Check if PRD.md contains a # Design System section. If it does:
[DESIGN_SYSTEM.md](reference/DESIGN_SYSTEM.md))<script> config block in shell.html (custom colors, fonts), all generated partials (consistent color classes), and component renderingIf PRD.md does not have a # Design System section, or the referenced file does not exist, fall back to the application's <app_folder>/context/design/ folder. This folder contains pre-defined design tokens and Tailwind component guidelines maintained externally by the UI/UX team.
Read all files in {app_name}/context/design/ (where {app_name} is the resolved application folder name from Step 1). Apply the design tokens and guidelines found there to all generated mockup screens.
Expected files (any or all may be present):
design-system.md — Colors, typography, spacing, and visual style definitionscomponents.md — Reusable component patterns and Tailwind class conventionsguidelines.md — Layout rules, accessibility standards, and stack-specific guidelinesIf neither the PRD reference nor the {app_name}/context/design/ folder provides design tokens, use sensible defaults: a neutral color palette, Inter/system font stack, and standard Tailwind utility classes for spacing and layout.
If PRD.md contains a # High Level Process Flow section, scan it for entity status lifecycle descriptions (e.g., "Received → Validated → Enriched → Active"). For each status lifecycle found:
For each role, determine ALL screens to generate. Every clickable link, tab, or action
in any generated screen MUST have a corresponding content fragment file. No link may point
to # or be a dead end.
Module filter applied here: If a module argument was provided (Step 1c), plan only the screens for that module across all roles. Skip common screens (home, profile, account, notifications) and skip all other modules entirely. The screen plan table should list only the filtered module's screens.
For each module screen, analyze the user stories and identify sub-screens needed:
| User Story Pattern | Sub-Screen Required |
|---|---|
| "view details of X" | {module}_detail.html - Detail view for a single record |
| "add/create/register X" | {module}_create.html - Create/add form |
| "edit/update/modify X" | {module}_edit.html - Edit form (pre-filled) |
| "view history/audit of X" | {module}_history.html - History/audit log view |
| "view associated X of Y" | {module}_{sub}_list.html - Associated records list |
Scan PRD.md for report-related content:
If report requirements are found, generate HTML report layout mockups for each identified report. These layouts serve as draft previews for human designers/stakeholders to verify the report structure before the AI coding agent implements the actual report generation code (JasperReports JRDesign API for Java, Puppeteer for Laravel/React).
For each identified report, create a standalone HTML file in a reports/ subfolder:
| Report Source | File Generated |
|---|---|
| NFR describes "Staff Allocation Summary report" | reports/staff_allocation_summary.html |
| User story: "generate Job Demand report by country" | reports/job_demand_by_country.html |
| Report module NFR: "Monthly Activity Report" | reports/monthly_activity_report.html |
Report layout file conventions:
<html>, <head>, <body> tags and Tailwind CDN <script> in the head<body class="bg-gray-100">
<div class="mx-auto bg-white shadow" style="width: 210mm; min-height: 297mm; padding: 15mm;">
<!-- Report content -->
</div>
</body>
model/{module}/model.md exists, use its field definitions for column headers)style="width: 297mm; min-height: 210mm;"Report parameter section: Above the report data, include a gray-shaded "Parameters" box showing the filter criteria used to generate the report (e.g., Date Range: 2025-01-01 to 2025-12-31, Department: All, Status: Active).
Add to MOCKUP.html: Include a "Reports" section at the bottom of each role's screen cards (after all module cards) listing the report layout links. Report links open in new tabs pointing directly to the standalone HTML files (no server route needed — static files).
Add to sidebar: If reports are present, add a "Reports" navigation group in each role's sidebar with links opening report layouts in new tabs.
If a module screen contains tabs (e.g., a detail page with Overview, Documents, History tabs),
each tab MUST be a separate content fragment file unless the tab content is trivially small.
Use the naming convention: {module}_tab_{tab_name}.html
Example: employer_tab_overview.html, employer_tab_documents.html, employer_tab_history.html
Convert names to snake_case, no .html extension in HTMX route references.
Example: "Location Information" -> location_information
Build a complete screen plan. Every entry must map to a generated file:
| Role | Folder Name | Screen File | Source | Description |
|---|---|---|---|---|
| Hub Administrator | hub_administrator | home | Common | Dashboard home |
| Hub Administrator | hub_administrator | profile | Common | User profile |
| Hub Administrator | hub_administrator | account | Common | Account settings |
| Hub Administrator | hub_administrator | notifications | Common | Notifications list |
| Hub Administrator | hub_administrator | location_information | Module | USHM00006, USHM00009 |
| Hub Administrator | hub_administrator | location_information_detail | Sub-screen | View location details |
| Hub Administrator | hub_administrator | location_information_create | Sub-screen | Add new location |
| Hub Operation Support | hub_operation_support | home | Common | Dashboard home |
| Hub Operation Support | hub_operation_support | profile | Common | User profile |
| Hub Operation Support | hub_operation_support | account | Common | Account settings |
| Hub Operation Support | hub_operation_support | notifications | Common | Notifications list |
| Hub Operation Support | hub_operation_support | employer | Module | USHM00021-USHM00033 |
| Hub Operation Support | hub_operation_support | employer_detail | Sub-screen | View employer details |
| Hub Operation Support | hub_operation_support | employer_create | Sub-screen | Register new employer |
Module filter: When a module argument is active, skip this step entirely — the folder structure already exists from a previous full generation. Only content files for the target module will be written in Step 6e.
Create the mockup folder at the auto-resolved mockup output path (full generation only):
<app_folder>/context/
mockup/
.gitignore # Git ignore for node_modules, etc.
server.js # Express server
package.json # Node.js dependencies
MOCKUP.html # Landing/index page (static)
partials/
shell.html # Page shell (assembled server-side)
header.html # Top header with Alpine.js dropdowns
footer.html # Footer partial
sidebar-{role_snake_case}.html # One sidebar per role with HTMX nav
{role_snake_case}/
content/
home.html # Content fragment (no layout wrapper)
profile.html
account.html
notifications.html
{module_snake_case}.html
{module_snake_case}_detail.html
{module_snake_case}_create.html
{module_snake_case}_edit.html
...
Module filter: When a module argument is active, skip this step entirely.
Generate .gitignore, server.js, and package.json using the templates from
references/admin-layout-template.md.
Create a .gitignore file in the mockup folder with the following content:
node_modules/
package-lock.json
.DS_Store
*.log
Key behaviours of server.js:
GET / → serves MOCKUP.html (static landing page)GET /:role → redirects to /:role/homeGET /:role/:page → assembles full HTML from shell + header + sidebar + content + footerGET /api/content/:role/:page → returns content fragment only (for HTMX in-page swaps){{ROLE}} into header and sidebar partials before responding (so HTMX links in
dropdowns and sidebar reference the correct role)Module filter: When a module argument is active, do NOT regenerate the full MOCKUP.html. Instead, apply a partial update as described in Step 1c: update only the target module's screen cards, the version badge, and the per-role screen count. Leave all other content unchanged.
Create the index page using the template in references/mockup-index-template.md.
MOCKUP.html is a standalone static HTML file (no server required to open it) that shows:
npm install && npm start)target="_blank" rel="noopener noreferrer" pointing to
http://localhost:3000/{role}/{page} so they open in new tabs via the running serverUse templates from references/admin-layout-template.md.
Apply the design system from Step 2 (colors, typography, spacing) to all templates.
Module filter: When a module argument is active, skip steps 6a–6d (shell, header,
footer, sidebars). Proceed directly to 6e for the target module's content fragments only.
Also update partials/footer.html if the version string changed (the footer version badge
must always reflect the current target version).
Single shared shell file. The server replaces {{HEADER}}, {{SIDEBAR}}, {{CONTENT}},
and {{FOOTER}} at request time. Includes HTMX, Alpine.js, and Tailwind CDN.
One header partial (or one per role if HTMX links differ per role). Contains:
x-data, @click, @click.outside)appShell() Alpine context)hx-get, hx-target="#content-area")Simple footer with copyright year and version string.
Each sidebar contains HTMX-powered navigation links:
<a href="/{{ROLE}}/{{PAGE}}"
hx-get="/api/content/{{ROLE}}/{{PAGE}}"
hx-target="#content-area"
hx-swap="innerHTML"
hx-push-url="/{{ROLE}}/{{PAGE}}"
...>
Alpine.js :class binding highlights the active menu item based on window.location.pathname.
Each content fragment contains only the page content — no <html>, <head>, <body>,
no Tailwind config, no CDN scripts. Structure:
[breadcrumb bar div]
[main content div with padding]
All navigation links within content fragments use HTMX (same pattern as sidebar).
Breadcrumbs: Home link uses HTMX; module link uses HTMX; current page is plain text.
For each module screen, analyze the user stories and generate appropriate UI mockup elements:
| User Story Pattern | UI Element |
|---|---|
| "search for X based on parameters" | Search form with filter fields + results table |
| "view details of X" | Detail view with labeled fields in card/panel layout |
| "manage X" / "configure X" | CRUD table with Add/Edit/Delete actions |
| "view history/changes" | Timeline or audit log table with timestamps |
| "map X to Y" | Two-panel mapping interface or matrix table |
| "activate/deactivate X" | Alpine.js toggle switches in table rows or config panel |
| "view associated X" | Related records table or linked cards section |
When a module model was loaded in Step 1b, use its actual field definitions — not generic placeholders — to populate every screen. Generic field names like "Field 1" or "Description" are not acceptable when a model is available.
Field type → HTML input mapping:
| Model Type | Form Input | Notes |
|---|---|---|
String | <input type="text"> | Use maxlength if constraints specify length |
Number | <input type="number"> | |
Boolean | Alpine.js toggle (x-data, @click) | |
ISODate | <input type="date"> or <input type="datetime-local"> | |
ObjectId (reference) | <input type="text" readonly> or lookup widget | Display as read-only ID reference |
| Enum (Section 7 match) | <select> with all enum values as <option> | Show enum value descriptions as option text |
| Embedded Object | <fieldset> grouping sub-fields | Label the fieldset with the embedded type name |
Embedded Array ([]) | Repeatable section with "+ Add" / "Remove" buttons | Show one pre-filled example row |
List / Search screens ({module}.html):
<select>. Date-indexed fields use date range pickers.—) in sample data.{module}_detail, Edit → {module}_edit, Delete → Alpine.js confirm<select>) with options 10, 25, 50 on the right (default 10)... ellipsis for gapsx-data="{ currentPage: 1, pageSize: 10, totalItems: 47 }" (sample total) to drive display statehx-get linking to the same route (self-referential) — acceptable per Link Integrity Rule 6opacity-50 cursor-not-allowed) when at first/last pageDetail screens ({module}_detail.html):
address, contact): each in its own labelled sub-cardpersonsInCharge): rendered as a sub-table with a row per itemDD MMM YYYY HH:mm in sample dataCreate screens ({module}_create.html):
Required: Yes non-system fields as mandatory inputs (mark with *)Required: No fields as optional inputs where they make sense for initial creation<fieldset> sections with a legend{module} list screenEdit screens ({module}_edit.html):
companyRegistrationNumber) must be rendered as <input readonly> with a tooltip explaining they cannot be changedHistory / Audit screens ({module}_history.html):
changeType → colored badge using enum values from Section 7fieldChanged → code-styled text (<code>)previousValue / newValue → inline diff or truncated JSON displaychangedAt → formatted timestampchangedBy → plain text (e.g., "SYSTEM" or username)Sample data alignment: Placeholder values in screens must be consistent with field constraints:
countryCode → use actual allowed values (e.g., "MYS", "BHR", "MDV") per CONSHM constraints if present in model notescompanyRegistrationNumber → e.g., "201901012345 (1234567-X)"ISODate fields → use realistic ISO dates (e.g., "2025-08-15T10:30:00Z")Every clickable element MUST navigate to a real route. No href="#" allowed anywhere.
Table row actions (View, Edit, Delete):
/{role}/{module}_detail/{role}/{module}_edithref="javascript:void(0)")/{role}/{module}_createTabs within a screen:
Header links:
notificationsnotificationsjavascript:void(0) (static language switcher mockup)@click="toggleDark()" (no href)profileaccounthref="/" (returns to landing page)Sidebar links: HTMX links to correct module screen routes (already enforced)
Breadcrumb links: HTMX links
/{role}/home/{role}/{module}Pagination links: HTMX hx-get links to the same route (self-referential) are acceptable. Pagination is MANDATORY on every list — page buttons must use hx-get with the same route, not href="#"
Back / Cancel buttons: HTMX link to the parent screen
Images (inline previews, thumbnails, etc.):
<a href="..." target="_blank" rel="noopener noreferrer">https://placehold.co/800x600PDFs and documents (download/view links):
<a href="..." target="_blank" rel="noopener noreferrer">Content guidelines:
<!-- USHM00012 [v1.0.1] -->)x-data, x-show, @click, :class)profile.html: User profile page showing:
account.html: Account settings page showing:
notifications.html: Notifications list page showing:
{module}_detail.html: Record detail page showing:
{module}){module}_edit), Delete (Alpine.js confirm), Back to List{module}_create.html: Create/add form page showing:
{module}){module}_edit.html: Edit form page showing:
{module}_detail or {module})After generation, print a summary:
Mockup Generation Complete
===========================
Application: {App Name} ({Initials})
Target Version: {version or "latest (all versions)"}
Module Filter: {module name or "all modules"}
Output: {path}/mockup/
Filtering Summary:
- User stories included: {count}
- User stories excluded (strikethrough): {count}
- User stories excluded (version filter): {count}
- User stories excluded (module filter): {count}
- NFRs/Constraints included: {count}
- NFRs/Constraints excluded: {count}
| Role | Screens | Content Folder |
|-----------------------|---------|-----------------------------------|
| Hub Administrator | 8 | hub_administrator/content/ |
| Hub Operation Support | 6 | hub_operation_support/content/ |
Files generated:
- .gitignore
- server.js + package.json
- partials/shell.html, header.html, footer.html
- partials/sidebar-hub_administrator.html
- partials/sidebar-hub_operation_support.html
- {N} content fragment files
Total: {N} files
Quick Start
===========
1. cd {mockup folder path}
2. npm install
3. npm start
4. Open http://localhost:3000 in your browser
(or open MOCKUP.html directly for the index)
Before finalizing output, perform a link integrity check across ALL generated files:
href, hx-get, and hx-push-url attribute valueshx-get="/api/content/{role}/{page}" → verify {role}/content/{page}.html existshref="javascript:void(0)" → acceptable for delete confirm, locale switcherhref="/" → acceptable for logouttarget="_blank" links → acceptable for images, PDFs, external resourceshref="#" → NOT ALLOWED — dead link, must be fixedIf any href="#" is found in the final output (excluding anchor-only usage), the generation
is incomplete.
After all mockup files are successfully generated, append an entry to CHANGELOG.md in the application folder (<app_folder>/CHANGELOG.md):
<app_folder>/CHANGELOG.md. If it does not exist, create it with:
# Changelog
- This file tracks all skill executions by version for this application.
- The highest version recorded here is the current application version.
- Skills MUST NOT execute for a version lower than the highest version in this file.
---
## {version} heading matching the current version.--- below the context header and before any existing ## vX.Y.Z section (newest-first ordering), with a new table header and the first row.| {YYYY-MM-DD} | {application_name} | mockgen-tailwind | {module or "All"} | Generated HTML mockup screens |module: is specified, only the named
module's content files are written/overwritten. All other files (partials, other module
content, server.js) remain untouched. If the target module does not exist in PRD.md,
stop and report available module names before doing any file writes.module:Employer v1.0.2 means "generate Employer module screens as they exist at v1.0.2". Version filtering
and module filtering each apply independently; a story must satisfy BOTH to be included.href and hx-get in every file must resolve. No href="#"x-data for page state and HTMX hx-get (self-referential) for page navigation. Omitting pagination from any list is a generation error.<html>/<head>/<body>hx-get, hx-target, hx-push-url)<img> link or image view action uses target="_blank"target="_blank" rel="noopener noreferrer"target="_blank"https://placehold.co/ for placeholder images<!-- USHM00012 [v1.0.1] -->)~~ are deprecated/removedmodel/{kebab-module}/model.md,
use the actual field definitions (field names, types, required/nullable, enums) for all
form inputs, table columns, and detail panels. Generic placeholder field names are NOT
acceptable when a model is available. See Step 1b and the "Model-Driven Field Usage" section.MYS, BHR, MDV for countryCode fields constrained by CONSHM018)reports/ subfolder. These are self-contained A4
mockups (not content fragments) for stakeholder review of report structure before coding.
Use actual module model fields for column headers and realistic sample data rows. See
Step 3f for full report layout generation rules.npx claudepluginhub rashidee/co2-skills --plugin co2-skillsProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.