agentleFS
danieletetidanieleteti/delphi-ai-skills/skills/dmvcframework-ui/SKILL.md

dmvcframework-ui

Skill23 starsChanged 7 days agoApache-2.0View on GitHub

Use when styling or laying out the HTML of a DMVCFramework web application — adding a page, a nav entry, a card, a form, a toast, a theme toggle, or touching style.css. Covers the presentation layer the DMVCFramework wizard actually generates: Bootstrap 5.3 (loaded from CDN in baselayout), the brand tokens in style.css, dark/light mode via data-bs-theme, and the HTMX activity classes. Triggers on "style.css", "Bootstrap", "dark mode", "theme toggle", "navbar", "card", "toast", "hero", "CSS", "layout", "responsive", "look and feel", "brand colors".

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before writing any markup or CSS, know these three facts:

    line 9
  2. Very specific:

    Never invent an identifier or answer from memory.

    line 33
  3. Distinctive:

    If you still cannot verify it, say so.

    line 38
  4. Very specific:

    Every page does {{extends "../baselayout.html"}} and fills blocks.

    line 44
  5. Very specific:

    Keep aria-current — it is what a screen reader announces.

    line 105
  6. Very specific:

    Do not hand-build a JSON string for the payload — you get a *quoted string* on the client, not an object.

    line 197

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

DMVCFramework — UI layer (Bootstrap 5.3 + style.css)

The wizard's web application ships a complete presentation layer. Use it — do not invent one. Before writing any markup or CSS, know these three facts:

  1. Bootstrap 5.3 is already loaded from CDN in baselayout.html (CSS in <head>, bootstrap.bundle.min.js before </body>). Every stock component — grid, cards, navbar, buttons, forms, alerts, badges, modals, spinners, toasts — is available. Do not add another CSS framework, and do not hand-roll what Bootstrap has.
  2. style.css is deliberately small (~100 lines). It is not a design system: it sets brand tokens, repaints Bootstrap's primary colour, and adds four helpers. Read it before adding anything to it.
  3. Dark mode is Bootstrap's, driven by data-bs-theme on <html>. Never write a .dark-mode class or a prefers-color-scheme media query — use Bootstrap's theme-aware variables and they follow the toggle for free.

STOP — this skill works inside a wizard-generated project. It describes the layout and stylesheet the DMVCFramework IDE wizard produces. If there is no wizard project in the current folder (no bin/templates/baselayout.html, no bin/www/css/style.css), stop and tell the user to create one first (Delphi IDE → File → New → Other → Delphi Projects → DelphiMVCFramework → New DMVCFramework Application, preset Web Application, defaults accepted — the server backend is Indy Direct), then cd into the folder and start you there. Read baselayout.html and style.css before writing any markup or CSS.

COMPANION SKILL: dmvcframework-webapp — the server side (TemplatePro syntax, RenderView, ViewData, HTMX, controllers). This skill is only about what the browser renders.


When in doubt about an API — verify it, never guess

Never invent an identifier or answer from memory. If you need a signature this skill does not cover, ask the user for the path to their DelphiMVCFramework checkout and read sources/ (and the matching samples/ project); failing that, read the official repository — sources · samples. If you still cannot verify it, say so. Where a skill and a sample disagree, the sample wins.


1. What baselayout.html gives you

Every page does {{extends "../baselayout.html"}} and fills blocks. The layout already provides the <!DOCTYPE>, <head>, the navbar, <main class="container">, the footer, the toast container, Bootstrap's JS, the theme toggle and the no-flash theme restore script.

Blocks you override:

BlockPurpose
titlePage title in the browser tab
bodyThe page content — this is the one you always fill
extra_cssExtra <link>/<style> for this page only
scriptsPage-specific <script> before </body>
extra_jsExtra JS after scripts

Variables the controller sets (available in any template): app_name, dmvc_version, current_year, page_id.

A new page is just this:

{{extends "../baselayout.html"}}

{{block "title"}}Customers{{endblock}}

{{block "body"}}
<h1 class="mb-4">Customers</h1>
<div class="row g-3">
  {{for c in customers}}
  <div class="col-md-4">
    <div class="card h-100">
      <div class="card-body">
        <h5 class="card-title">{{:c.FirstName}} {{:c.LastName}}</h5>
        <p class="card-text text-secondary">{{:c.Email}}</p>
      </div>
    </div>
  </div>
  {{endfor}}
</div>
{{endblock}}

No <html>, no <head>, no stylesheet link, no container div. The layout owns those.


2. The navbar — adding an entry

Nav links live in baselayout.html. The active state is driven by page_id, which the controller sets:

<li class="nav-item">
  <a class="nav-link {{if page_id|eq,"customers"}}active{{endif}}"
     {{if page_id|eq,"customers"}}aria-current="page"{{endif}}
     href="/web/customers">Customers</a>
</li>
ViewData['page_id'] := 'customers';

Keep aria-current — it is what a screen reader announces. The nav is navbar-expand-md: it collapses into the burger toggler below md, which already works. Do not remove the toggler.


3. Theme (dark / light)

<html data-bs-theme="dark"> is the default; a toggle button in the navbar flips it and persists the choice in localStorage['dmvc-theme'], and an inline script in <head> restores it before first paint (no flash).

To make your markup theme-aware, use Bootstrap's semantic classes and variables — never hard-coded colours:

Instead ofUse
background: #fffclass="bg-body" / bg-body-tertiary
color: #333class="text-body" / text-secondary
border: 1px solid #dddclass="border"
a custom colour in CSSvar(--bs-body-bg), var(--bs-border-color), var(--bs-secondary-color)

These all flip automatically with the theme. A literal hex does not.


4. style.css — what is in it, and what to add

:root {
  --brand-accent: #fbbf24;          /* amber-400 — the brand colour */
  --brand-accent-strong: #d4991a;
  --brand-font-body: 'Outfit', system-ui, sans-serif;
  --brand-font-mono: 'JetBrains Mono', ui-monospace, monospace;
}
  • Bootstrap's primary is repainted to the accent (--bs-primary, --bs-link-color), so .btn-primary, .text-primary and links are already gold. Use btn-primary; do not write a custom gold button. (Note the file also overrides .btn-primary's own --bs-btn-* vars explicitly — Bootstrap 5.3 bakes button colours at compile time, so overriding --bs-primary alone would not repaint them.)
  • Fonts: Outfit for body, JetBrains Mono for code/pre/kbd/samp, imported from Google Fonts.

The four helpers it defines — use them instead of reinventing:

ClassWhat it is
.heroCentred landing block with a subtle accent gradient. <div class="hero"><h1>…</h1><p class="lead">…</p></div>
.section-labelSmall mono uppercase marker above a section
.badge.brand-monoMonospace badge, for versions/ids
.htmx-request / .htmx-indicatorHTMX activity: the element dims while its request is in flight; .htmx-indicator shows only during it

Before adding a rule to style.css, check in this order: does a Bootstrap utility do it (spacing m-*/p-*, flex d-flex, text, colour)? Does a Bootstrap component do it? Only then write CSS — and write it against the brand tokens and --bs-* variables, not literals.


5. Toasts

The layout defines #toastContainer and a global showToast(message, type) — type is success, danger, warning or info. message goes into innerHTML: it is HTML, not text. A fixed string chosen by the server is safe; anything a user typed (a name, a title) must be escaped first, or it is a DOM XSS. From an HTMX response, trigger it with a client event from Delphi:

uses MVCFramework.HTMX;

// The payload is serialized as a JSON *value*. A string arrives at the client as evt.detail.value —
// NOT as evt.detail.<something>. This is the shape the framework's own sample uses.
Context.Response.HXTriggerClientEvent('showMessage', 'Customer saved');
{{# in the page's "scripts" block #}}
<script>
  document.body.addEventListener('showMessage', function (evt) {
    showToast(evt.detail.value, 'success');
  });
</script>

Need structured data instead of a plain string? Pass an object and read its properties:

Context.Response.HXTriggerClientEvent('customerSaved', lCustomer);   // a Delphi object
function escapeHtml(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }

document.body.addEventListener('customerSaved', function (evt) {
  showToast('Saved ' + escapeHtml(evt.detail.FirstName), 'success');
});

Do not hand-build a JSON string for the payload — you get a quoted string on the client, not an object. And do not write your own toast markup or alert().


6. Forms

Stock Bootstrap, plus HTMX for the submit. Server-side validation errors come back as HTML.

<form hx-post="/web/customers" hx-target="#form-wrap" hx-swap="outerHTML" class="row g-3">
  <div class="col-md-6">
    <label for="firstName" class="form-label">First name</label>
    <input type="text" class="form-control {{if errors.FirstName}}is-invalid{{endif}}"
           id="firstName" name="firstName" value="{{:customer.FirstName}}" required>
    {{if errors.FirstName}}<div class="invalid-feedback">{{:errors.FirstName}}</div>{{endif}}
  </div>
  <div class="col-12">
    <button type="submit" class="btn btn-primary">Save</button>
  </div>
</form>

is-invalid + .invalid-feedback is Bootstrap's error pattern — it needs no JS. Every input needs a <label for>.

In a wizard project prefer the generated forms library, bin/templates/lib/forms_bootstrap5.tpro ({{import "../lib/forms_bootstrap5.tpro" as f}} from a page in a subfolder — the path is relative to the page — then f.input, f.select, f.checkbox, …): it writes this markup and reads values from formModel and messages from formErrors, both keyed by the control's name. Whatever you use, the error-map keys must be the ones the server fills — the wizard's convention is the lowercase field name (server side: the dmvcframework-webapp skill, "Form posts").


7. Common mistakes

MistakeWhy it is wrong
Adding Tailwind / Pico / a second frameworkBootstrap 5.3 is already loaded. Two frameworks = two resets fighting.
Writing a custom grid / flexbox layoutrow/col-*, d-flex, gap-* already exist.
Hard-coded #fff / #333 in CSS or style=""Breaks in the other theme. Use bg-body / text-body / --bs-* vars.
A .dark-mode class or prefers-color-scheme queryDark mode is data-bs-theme. Yours will not follow the toggle.
A custom gold button.btn-primary is already the brand accent.
Hand-rolled toast / modal / dropdownBootstrap ships them; showToast() is already global.
<link>ing style.css or Bootstrap in a page templateThe layout does it. A child template only fills blocks.
Restyling .htmx-requestIt is already wired to the HTMX request lifecycle.

8. Files

FileWhat it is
bin/templates/baselayout.htmlThe layout: head, navbar, main, footer, toasts, theme toggle, Bootstrap JS
bin/www/css/style.cssBrand tokens, primary override, 4 helpers, HTMX classes (~100 lines)
bin/www/Static root, served at /static by FileServerBundle (TMVCStaticFilesMiddleware in a 3.4 project)

Bootstrap docs: https://getbootstrap.com/docs/5.3/ — read the component page rather than guessing class names.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.