mockgen-tailwind — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mockgen-tailwind (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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)and references from sections whose version tag is less than or equal to the target version
module:Location Information), only generate/update screensfor 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:
Strip leading number and underscore prefix, then title-case. Example: 1_hub_middleware -> "Hub Middleware"
Example: 1_hub_middleware -> "HM"
## Module Name section under a # Module Category heading.Record the module name and its description (the line after the heading).
- [USxx#####] As a {Role} user, I want to...Extract: tag, role, action summary.
Example: "Hub Administrator", "Hub Operation Support"
filtering in the next sub-step.
#### 1a: Version Filtering and Strikethrough Exclusion (MANDATORY)
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...~~ → SKIPif the tag identifier is within the strikethrough
Version filtering (applied only when a target version is provided):
[v1.0.0] or [v1.0.1]v1.0.1):exclude strikethrough items)
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):
#### 1c: Module Filtering (applied only when a module argument is provided)
When a module argument is present, apply module filtering after version filtering:
leading/trailing whitespace). Also accept snake_case input by converting it to title-case for comparison (e.g., location_information → match "Location Information").
All other steps complete normally (parsing, design system, planning) but output is restricted to the filtered module's screens.
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):
existing card tags)
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:
location-information, "Industrial Classification" → industrial-classification, "Employer" → employer{model_dir}/{kebab-module}/model.mdField 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:
#### 2a: PRD.md Design System Reference (Primary Source)
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 rendering#### 2b: Context Design Folder (Fallback)
If 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 guidelines#### 2c: Default Fallback
If 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.
#### 2d: Process Flow Status States
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.
#### 3a: Core Screens (content fragments)
#### 3b: Sub-Screens (Detail / Edit / Create)
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 |
#### 3f: Report Layout Screens (conditional — if PRD.md contains report-related content)
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:
with its own <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>(if 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.
#### 3c: Tabbed Screens
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
#### 3d: Screen File Naming Convention
Convert names to snake_case, no .html extension in HTMX route references. Example: "Location Information" -> location_information
#### 3e: Build Screen Plan
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.
#### .gitignore
Create a .gitignore file in the mockup folder with the following content:
node_modules/
package-lock.json
.DS_Store
*.logKey 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 indropdowns 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)http://localhost:3000/{role}/{page} so they open in new tabs via the running server
Use 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).
#### 6a: Generate partials/shell.html
Single shared shell file. The server replaces {{HEADER}}, {{SIDEBAR}}, {{CONTENT}}, and {{FOOTER}} at request time. Includes HTMX, Alpine.js, and Tailwind CDN.
#### 6b: Generate partials/header.html
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")#### 6c: Generate partials/footer.html
Simple footer with copyright year and version string.
#### 6d: Generate partials/sidebar-{role}.html (one per role)
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.
#### 6e: Generate content fragments ({role}/content/{page}.html)
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.
#### Admin Layout: Screen Content Generation
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 |
#### Model-Driven Field Usage (MANDATORY when model file exists)
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")#### Link Integrity Rules (CRITICAL)
Every clickable element MUST navigate to a real route. No `href="#"` allowed anywhere.
/{role}/{module}_detail/{role}/{module}_edithref="javascript:void(0)")/{role}/{module}_createnotificationsnotificationsjavascript:void(0) (static language switcher mockup)@click="toggleDark()" (no href)profileaccounthref="/" (returns to landing page)/{role}/home/{role}/{module}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="#"<a href="..." target="_blank" rel="noopener noreferrer">https://placehold.co/800x600<a href="..." target="_blank" rel="noopener noreferrer">Content guidelines:
(e.g., <!-- USHM00012 [v1.0.1] -->)
x-data, x-show, @click, :class)#### Common Screens Content
profile.html: User profile page showing:
account.html: Account settings page showing:
notifications.html: Notifications list page showing:
#### Sub-Screen Content
{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 namedmodule'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.
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(e.g., <!-- USHM00012 [v1.0.1] -->)
found in PRD.md
~~ are deprecated/removedwith version tags <= target version
model/{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.
Definitions (Section 7), not inferred strings
model (e.g., use MYS, BHR, MDV for countryCode fields constrained by CONSHM018)
standalone HTML report layout files in 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.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.