AI Assistant report actions (mode: report / report-review) open a host tab and render server-built HTML. Redactor ships a small CSS framework for that HTML so reports look consistent in the tab without inventing one-off markup.
Related: Writing AI Assistant prompts · Backend examples · Using the AI assistant
Contract#
Your backend returns a JSON envelope. The editor uses only the report shell fields for the tab:
{
"title": "Writing analysis",
"tab": "writing-analysis",
"html": "<div class=\"rx-ai-report\">…</div>"
}
| Field | Role |
|---|---|
title |
Tab label |
tab |
Stable tab key |
html |
Full report markup (styles come from Redactor CSS) |
The model should return structured data. Your server turns that data into html using the classes below. Do not ask the model for a free-form page layout.
For report-review, the same html goes in the report tab; issues still drive the review cards.
Naming#
All report classes use the Redactor rx- prefix. CSS variables use --rx-.
| Kind | Pattern | Example |
|---|---|---|
| Block | rx-ai-report |
Root wrapper |
| Element | rx-ai-report__… |
rx-ai-report__finding |
| Modifier | rx-ai-report__…--… |
rx-ai-report__table--5 |
| Semantic state | is-positive / is-negative / is-warning / is-info |
Meaning, not decoration |
| Variables | --rx-report-… |
--rx-report-bg, --rx-report-red |
Always wrap the whole report in one root:
<div class="rx-ai-report">
…
</div>
Styles are scoped under .rx-ai-report and ship with redactor.css.
Building blocks#
| Class | Role |
|---|---|
rx-ai-report__header |
Title area (label + h1 + lead) |
rx-ai-report__section |
Vertical section spacing |
rx-ai-report__content |
Plain prose (h2 + paragraphs) |
rx-ai-report__panel |
Surface box; modifiers --soft, --dark |
rx-ai-report__grid |
Multi-column layout; --3 / --4 / --5 / --flush |
rx-ai-report__metric |
Single KPI inside a grid |
rx-ai-report__finding |
Main reusable insight unit |
rx-ai-report__compare |
Weak / strong side-by-side |
rx-ai-report__callout |
Short highlighted note |
rx-ai-report__list |
Bullets; --plain for unmarked items |
rx-ai-report__steps |
Numbered action list |
rx-ai-report__quote |
Closing pull-quote |
rx-ai-report__table |
CSS grid “table” (not <table>); --2…--5 |
rx-ai-report__cell |
Cell inside the table |
rx-ai-report__badge |
Severity / status chip |
rx-ai-report__label |
Small uppercase / muted label |
rx-ai-report__lead |
Supporting paragraph under the title |
rx-ai-report__meta |
Secondary metadata text |
Use findings for teachable issues, content for narrative, panels when you need a surface behind lists or steps.
Semantic states#
Attach one state class when the meaning is positive, negative, warning, or informational:
<span class="rx-ai-report__badge is-negative">High</span>
<div class="rx-ai-report__finding is-warning">…</div>
<div class="rx-ai-report__callout is-info">…</div>
<div class="rx-ai-report__cell-value is-positive">Good</div>
States tint text, badges, finding borders, compare columns, and callouts. Prefer meaning over decoration — do not use is-negative only for red styling.
Minimal report#
<div class="rx-ai-report">
<header class="rx-ai-report__header">
<div class="rx-ai-report__label">Writing analysis</div>
<h1>The text feels generic</h1>
<p class="rx-ai-report__lead">
It reads smoothly but rarely shows a concrete observation or a clear stance.
</p>
</header>
<section class="rx-ai-report__content">
<h2>Why it feels templated</h2>
<p>Most sentences stay at the level of general claims.</p>
</section>
</div>
Score row (table cells)#
Use the CSS grid table — not HTML <table>:
<section class="rx-ai-report__section">
<div class="rx-ai-report__table rx-ai-report__table--5">
<div class="rx-ai-report__cell">
<div class="rx-ai-report__label">Quality</div>
<div class="rx-ai-report__cell-value is-negative">Very weak</div>
</div>
<div class="rx-ai-report__cell">
<div class="rx-ai-report__label">Clarity</div>
<div class="rx-ai-report__cell-value is-warning">Medium</div>
</div>
<div class="rx-ai-report__cell">
<div class="rx-ai-report__label">Structure</div>
<div class="rx-ai-report__cell-value is-positive">Good</div>
</div>
</div>
</section>
Finding#
The primary reusable unit for a check-writing style report:
<div class="rx-ai-report__finding is-negative">
<div class="rx-ai-report__finding-header">
<span class="rx-ai-report__badge is-negative">High</span>
<h2 class="rx-ai-report__finding-title">
The paragraph opens with a formula, not a thought
</h2>
</div>
<div class="rx-ai-report__finding-body">
<p class="rx-ai-report__finding-text">
“It’s a lottery” sounds familiar and does not explain the problem.
</p>
<div class="rx-ai-report__finding-action">
→ Start from a concrete observation and name what is happening.
</div>
</div>
</div>
Compare (weak / strong)#
<div class="rx-ai-report__compare">
<div class="rx-ai-report__compare-item is-negative">
<div class="rx-ai-report__label">Weak</div>
<div class="rx-ai-report__compare-text">Remote work is a lottery.</div>
</div>
<div class="rx-ai-report__compare-item is-positive">
<div class="rx-ai-report__label">Strong</div>
<div class="rx-ai-report__compare-text">
On remote days it is hard to tell when work ends — you just keep sitting with tasks.
</div>
</div>
</div>
Metrics, callouts, lists, steps#
<div class="rx-ai-report__grid rx-ai-report__grid--3">
<div class="rx-ai-report__metric">
<div class="rx-ai-report__label">Issues</div>
<div class="rx-ai-report__metric-value is-negative">7</div>
<div class="rx-ai-report__metric-note">Need attention</div>
</div>
</div>
<div class="rx-ai-report__callout is-warning">
<div class="rx-ai-report__callout-title">Watch the first sentence</div>
<p class="rx-ai-report__callout-text">
Opening with a cliché costs trust before the argument starts.
</p>
</div>
<div class="rx-ai-report__panel">
<div class="rx-ai-report__label">What to change</div>
<ul class="rx-ai-report__list rx-ai-report__list--plain">
<li>Replace general claims with observations</li>
<li>Give one concrete situation per paragraph</li>
</ul>
<ol class="rx-ai-report__steps">
<li>Cut the opening formula.</li>
<li>Add one specific detail.</li>
<li>Check that each paragraph carries a new thought.</li>
</ol>
</div>
Theming#
Override tokens on the root (or a parent) if you need brand colors. Defaults live on .rx-ai-report:
.rx-ai-report {
--rx-report-bg: #f5f5f5;
--rx-report-surface: #ffffff;
--rx-report-text: #202020;
--rx-report-red: #c83b32;
--rx-report-green: #26795c;
--rx-report-orange: #bd7200;
--rx-report-blue: #356f9f;
--rx-report-radius: 4px;
--rx-report-gap: 24px;
}
Keep class names stable so one server HTML builder works for every report action.
PHP helpers (sample endpoints)#
Redactor’s sample backends ship builders that emit this markup:
| File | Role |
|---|---|
endpoints/_ai-report-html.php |
Primitives: ai_report_wrap, ai_report_header, ai_report_finding, ai_report_compare, ai_report_list, ai_report_steps, … |
endpoints/_ai-shared.php |
Action mappers: ai_report_html_summarize, …_check_writing, … → { title, tab, html } |
Axly/Imperavi mirrors the same helpers as lib/ai-report-html.php + lib/ai-shared.php.
Example:
require_once __DIR__ . '/_ai-report-html.php';
$html = ai_report_wrap(
ai_report_header('Writing analysis', 'Writing check', $lead)
. ai_report_section(ai_report_finding($title, $text, $action, 'High', 'negative'))
. ai_report_section(ai_report_compare($weak, $strong))
. ai_report_section(ai_report_panel(
ai_report_label('What to do next') . ai_report_steps($actions)
))
);
Always escape through ai_html_escape() / the helpers — never concatenate model text raw.
Backend checklist#
- Parse the model’s structured JSON for the action.
- Escape all user/model text before inserting into HTML.
- Build markup only with
rx-ai-report*classes (andis-*states). - Wrap everything in a single
.rx-ai-reportroot (ai_report_wrap()). - Return
{ title, tab, html }(plusissuesforreport-review).