21 KiB
Editor
Route: /article/:id/edit. API: GET /v1/article/{id} · PATCH /v1/article/{id} · PATCH …/publish|unpublish · POST …/image · DELETE.
On mobile, Save and Publish sit in a sticky bottom bar and the details panel opens as a slideover. A session expiry mid-edit keeps the draft in sessionStorage and goes to /login?redirect=/article/12/edit. After logging in, the editor restores the draft and shows "Unsaved changes".
Visual reference: RenewCMS Screens Back-office.dc.html, groups "Editor". Frames: AppFrame.dc.html (shell, sidebar, topbar, drawer) plus the screen frame where listed in components.md.
Copy per state (generated from the board source, pass 2)
Every state below is drawn in light and dark (theme switch on the board), at 1440×900 and 390×844 (both frames sit side by side). Board: RenewCMS Screens Back-office.dc.html. Copy is exact; where a cell is empty the state reuses the fixed copy.
Fixed copy in the frame
- This article doesn't exist
- It may have been deleted, or the link is wrong.
- All articles
- Online
- Draft
- Retry
- Preview
- Save
- Delete
- Every bathroom is different, but the way we price them isn't. We start with a visit, measure everything, and
- write down what stays
- We also check the
- timber windows
- /articles/winter-checklist
- Edit
- Open
- Remove
- before any tiling starts.
- What goes into the price
- Stripping out and making good
- Plumbing moves, if any
- Tiles, fittings and a day for silicone
- /static/uploadedImages/3f9c…e1.jpg
- We don't add a margin on materials you choose yourself.
- Details
- Created
- 12 Mar 2026, 09:14
- Updated
- Today, 11:02
- ID
- Images
- Public API URL
- /v1/article/12
- Title
- Titles can't be changed after an article is created. To use a different title, create a new article and delete this one.
Editor (group "Editor", 41 states)
Route: /article/:id/edit. API: GET /v1/article/{id} · PATCH /v1/article/{id} · PATCH …/publish|unpublish · POST …/image · DELETE.
On mobile, Save and Publish sit in a sticky bottom bar and the details panel opens as a slideover. A session expiry mid-edit keeps the draft in sessionStorage and goes to /login?redirect=/article/12/edit. After logging in, the editor restores the draft and shows "Unsaved changes".
| # | State | Trigger | Copy |
|---|---|---|---|
| 1 | Loading | GET in flight. Skeletons for the title, content and details. | |
| 2 | Saved | Just opened or just saved. Save is disabled. | |
| 3 | Unsaved changes | Any edit. Publish becomes "Save and publish". Leaving asks for confirmation. | |
| 4 | Saving | Save, or Ctrl/⌘ S. | |
| 5 | Save failed | 500 or network failure. The changes are kept and Retry resends them. | |
| 6 | Very long article | No length limit. The count stays neutral and correct at 120,000 characters. Save and Publish stay enabled. | |
| 7 | Very long article: saving | Save with large content. The status shows the spinner and the page stays editable. | "Saving 120,000 characters. This can take a few seconds." |
| 8 | Very long article: saved | 200. Status reads Saved with the time. The count is unchanged. | |
| 9 | Very long article: save failed | 500, 413 or network failure. Nothing is lost; Retry resends the same content. | "Couldn't save. Your text is kept. Retry." |
| 10 | Preview | The Preview switch. Shows the title and body in the content scale, the way a public site might. | |
| 11 | Link: create from selection | Ctrl/⌘ K or the link button with text selected. Link text is prefilled from the selection; URL has focus. | "Link text" / "timber windows" / "URL" |
| 12 | Link: create with no selection | Nothing selected. Link text is empty and has focus. | "Link text" / "What the reader sees" / "URL" |
| 13 | Link: edit existing | Edit in the link popover, or Ctrl/⌘ K inside a link. Prefilled; "Remove link" is offered. | "Edit link" / "Save link" / "Link text" / "timber windows" / "URL" |
| 14 | Link: invalid URL | On submit or blur. Nothing is inserted. | "Link text" / "timber windows" / "URL" / "Enter a valid URL. Use http(s), mailto, a path or #anchor." |
| 15 | Link: empty text | On submit with empty link text. | "Link text" / "Enter the link text." / "What the reader sees" / "URL" |
| 16 | Link: long URL | The field truncates in the middle of the line; the full value is available by scrolling the field and in the title attribute. | "Edit link" / "Save link" / "Link text" / "planning guidance" / "URL" |
| 17 | Link button: default | Toolbar button, icon link, after the list buttons. Desktop 28 px, mobile 40 px. Text colour --ui-text-toned. The toolbar scrolls sideways on mobile (see "Toolbar overflow"). | |
| 18 | Link button: hover and tooltip | Pointer or keyboard focus after 400 ms. Tooltip "Insert link (Ctrl+K)"; on macOS it reads "Insert link (⌘K)". Touch has no tooltip. | |
| 19 | Link button: focus-visible | 2 px --ui-primary outline, 1 px offset inside the toolbar. Enter or Space opens the link dialog. | |
| 20 | Link button: active (cursor in a link) | The cursor or selection is inside a link: aria-pressed true, --rc-sand fill and --rc-on-sand icon. Pressing it opens the dialog in edit mode. | |
| 21 | Link button: disabled | An image is selected in the editor. The button is at 40% opacity, aria-disabled, no tooltip. Ctrl/⌘ K does nothing. | |
| 22 | Link popover and sheet | Click a link, or Enter on a focused link. Desktop: popover under the link with the URL and Edit, Open and Remove. Mobile (touch): bottom sheet with the link text, the URL and Edit link, Open link, Remove link at 48 px. Esc or a tap outside closes it. Remove asks for no confirmation; undo restores the link. | |
| 23 | Rendered link: default | Same as the published page: --ui-text-highlighted, 1 px underline, 3 px offset. | |
| 24 | Rendered link: hover | Underline thickens to 2 px. | |
| 25 | Rendered link: focus | Tab or arrow keys into the link: 2 px --ui-primary outline, 2 px offset. | |
| 26 | Rendered link: selected | Text selected across the link: --rc-sand fill, --rc-on-sand text. | |
| 27 | Toolbar overflow (390 px) | The full toolbar does not fit at 390 px. It scrolls sideways in one row (no wrap); a 24 or 32 px fade marks each hidden side. This frame is scrolled to the end, so the link, remove-formatting and Image buttons are visible. Desktop is unchanged. | |
| 28 | Upload: choose | The image button in the toolbar, or a file dropped on the editor. | "Add an image" / "Cancel" |
| 29 | Upload: crop and resize | A JPEG, PNG or WEBP passed the checks, and nothing is sent yet. The crop starts as the whole image and the width defaults to 1600 px. SVGs skip this step. | |
| 30 | Upload: crop to a ratio | 16:9 chosen. The frame keeps the ratio while it's dragged or resized. Arrow keys move it; Shift and arrow keys resize it. | |
| 31 | Upload: crop narrower than the width | The crop is smaller than the chosen width, so it keeps its own size. | |
| 32 | Upload: in progress | Upload image was pressed. The cropped, resized file is uploading. Closing the dialog cancels the upload. | "Add an image" / "Cancel upload" |
| 33 | Upload: rejected | Checked in the browser, so nothing is sent. | "Add an image" / "Cancel" / "Choose another file" |
| 34 | Upload: inserted | 201: the image goes in at the cursor, and the article now has unsaved changes. | "Image added" / "Save to keep it in the article." |
| 35 | Publishing | "Save and publish": the PATCH body, then the publish request. | "Saving and publishing…" |
| 36 | Published | 200: the badge flips, Unpublish is offered and a toast confirms. | "Article published" / ""How we price a bathroom renovation" is now online." |
| 37 | Unpublishing | Unpublish doesn't save pending edits. | "Unpublishing…" |
| 38 | Delete: confirm | Delete… from the overflow menu. After a 204, it goes to /article with the "Article deleted" toast. | "Delete this article?" / ""How we price a bathroom renovation" and its 3 images will be deleted. This can't be undone." / "Cancel" / "Delete article" |
| 39 | Leave with unsaved changes | Back, a nav link or closing the tab. | "Leave without saving?" / "Your changes to "How we price a bathroom renovation" haven't been saved." / "Discard changes" / "Keep editing" |
| 40 | Offline | The connection dropped. Writing continues, and Save and Publish wait. | |
| 41 | Not found (404) | Unknown id, or the article was deleted elsewhere. |
Changed in pass 3: site and authors
Every existing Editor frame that has the details sidebar shows the "Site and authors" section collapsed, as the SPA renders it: one summary line ("Inès Duarte") because the install has one site and the article has an author, so there is no Site select. In the Loading frame the header is shown with a skeleton summary. The Not found frame has no editor, so it has no section. On mobile the section lives in the Details sheet, closed by default, so the mobile editor frames do not show it until the sheet is opened. The group below holds only the states specific to the feature.
Editor: site and authors (group "Editor: site and authors", 16 states)
Route: /article/:id/edit. API: PATCH /v1/article/{id} {site_id, author_ids[]} · GET /v1/site/ · GET /v1/author/ · POST /v1/author/.
States specific to this feature. The collapsed default (one site, the article has an author) and the Loading and Not found states are drawn in the existing Editor frames, which show the section in the same place; they are not repeated here. The section is open when the install has more than one site or the article has no authors, and collapsed to one summary line otherwise; the choice is remembered per browser. On mobile it is closed by default. With one site the Site select is not rendered. Anyone who can edit the article can set its site and pick authors; "Create author…", the Create author dialog and its "This is me" checkbox are for owner and admin only. For any other role, the "Create my author profile" hint (state 4) creates the profile directly with POST /v1/author/me, with no dialog, then shows the author in the list with a toast. Authors are a reorderable list; the first is the primary byline.
| # | State | Trigger | Copy |
|---|---|---|---|
| 1 | Open: one site, one author | The section was opened. No Site select. | Authors · Add an author |
| 2 | Open: several sites | Site select above the authors, with the canonical host as helper text. | Site · Journal · journal.renew.example · Canonical host: journal.renew.example |
| 3 | Site select open | The select is open. Options show name and host; the default site is first. | Renew · renew.example · Journal · journal.renew.example · Projects · Archive |
| 4 | No authors, with hint | The article has no authors and the user has no author profile. The hint is dismissible and does not return once dismissed. "Create my author profile" creates the profile directly for every role (POST /v1/author/me, no dialog), then the author appears in the list and a toast confirms. | You don't have an author profile yet · Create one to be credited on your articles. · Create my author profile · No authors. This article will have no byline. |
| 5 | Many authors | Four authors. The first shows "Primary". Each row has a drag handle and a remove button. Long names truncate with an ellipsis and carry the full name in title and aria-label. | Primary · Drag to reorder. The first author is the primary byline. · Dr. Alexandra Konstantinopoulou-Whitfield |
| 6 | Add author: open | The Add an author combobox is open. Authors already on the article are checked and dimmed. "Create author…" is shown only to owner and admin. | Search authors · Create author… |
| 7 | Add author: no match | Typing a name that matches nobody. "Create author…" is shown only to owner and admin. | No author matches "Zoe Lambert". · Create author "Zoe Lambert"… |
| 8 | Add author: open (no create) | A role without author rights. The combobox lists the authors with no "Create author…" row, because that row is shown only to owner and admin. | Search authors |
| 9 | Add author: no match (no create) | A role without author rights types a name that matches nobody. Only the message shows; there is no create link, because "Create author…" is shown only to owner and admin. | No author matches "Zoe Lambert". |
| 10 | Add author: error | GET /v1/author/ failed. The article keeps its authors. | Couldn't load authors · Check your connection and try again. · Try again |
| 11 | Reorder: keyboard | Space or Enter on the handle grabs the row. Arrow keys move it. | Jonas Pereira grabbed. Position 2 of 3. Use the up and down arrow keys to move, Space to drop, Escape to cancel. |
| 12 | Reorder: pointer | Dragging a row. The row lifts and a line shows the drop position. | |
| 13 | Create author: dialog | "Create author…" in the dropdown, owner and admin only. The writer stays in the editor. | Create author · Adds a byline for this article. You can add a bio and avatar later in Authors. · Name · Slug · Zoe Lambert · zoe-lambert · Cancel · Create author |
| 14 | Create author: for me | Owner and admin only. When the user has no author profile, a checkbox links the new author to them. | This is me · Links the author to marta@renew.example. |
| 15 | Create author: slug taken | The slug exists. The dialog suggests a free one. | The slug "zoe-lambert" is already used by another author. · Use zoe-lambert-2 |
| 16 | Create author: failed | 500 or network failure. | Couldn't create the author · The article is unchanged. Try again in a moment. |
Editor: search and sharing (SEO round)
Route: /article/:id/edit. API: PATCH /v1/article/{id} {slug, excerpt, seo_title, seo_description, cover_image_id, cover_image_alt, social_title, social_description, social_image_id, canonical_url, noindex, seo_dismissed[]}.
RenewCMS is headless: it never renders pages or adds meta tags. This section helps the writer fill in SEO data and shows what the frontend will receive. Everything is optional and each empty field shows its fallback. Placement: a "Search and sharing" section under "Site and authors" in the sidebar and in the mobile Details sheet. Collapsed by default with a one-line summary (Ready, N suggestions, Hidden from search), remembered per browser. The fields and live previews open in a slide-over (desktop, 960 px) or a 92% sheet over the Details sheet (mobile). Anyone who can edit the article can edit these fields. Suggestions never block saving or publishing: publishing is allowed regardless. Counters are soft guides and turn amber, never red, outside the recommended range. Light and dark follow the theme switch above.
| # | State | Trigger | Copy |
|---|---|---|---|
| 1 | Collapsed: ready | The default. Nothing to suggest. | Search and sharing · Ready |
| 2 | Collapsed: suggestions | Summary in the warning colour. No count badge elsewhere. | Search and sharing · 2 suggestions |
| 3 | Collapsed: hidden | noindex is on. | Search and sharing · Hidden from search |
| 4 | Open: ready | Address, the all-good message and two actions. | Address · renew.example/how-we-price-a-bathroom-renovation · All good · Nothing to suggest for this article. · Edit search and sharing · What the frontend gets |
| 5 | Open: suggestions | At most three suggestions here, then a count. Each links to its field. | The meta title is 74 characters. Search engines may cut it off after about 60. · Go to meta title · The cover image has no alt text. Add a short description of it. · Go to cover image · The body has no headings. H2 headings help readers scan the article. · Go to body · +1 more in Search and sharing |
| 6 | Open: checking | While the checks run after the editor loads. The rest of the section stays usable. | Checking this article… |
| 7 | Open: hidden | A line explains the flag. | Hidden from search engines. This only sets a flag in the API. |
| 8 | Open: other site | The address uses the article's site. | journal.renew.example/how-we-price-a-bathroom-renovation |
| 9 | Panel: defaults | A writer who changed nothing. Every value is a fallback and says so. | Search and sharing · URL slug · Generated from the title until you edit it. · Meta title · Using article title · Recommended 30 to 60 characters. This is a guide, not a limit. · Meta description · Using first sentence of the body · Recommended 70 to 160 characters. · Excerpt · A short summary. It is also sent as the article teaser. · Cover image · Using the site social image (north-wall.jpg). · Social sharing · Optional · Uses the meta values · Advanced · Canonical URL: automatic · Hide from search engines · Sets noindex in the API. It doesn't change who can read the article. · Search result · Social card · Suggestions · What the frontend gets · Cancel · Save changes |
| 10 | Panel: filled in | Own values everywhere. The fallback chips are gone and the suggestion list shows All good. | All good · Nothing to suggest for this article. |
| 11 | Panel: outside the soft ranges | Counters turn amber. The preview shows the cut-off. Nothing is blocked. | The meta title is 74 characters. Search engines may cut it off after about 60. · The description is 41 characters. About 70 to 160 reads best in results. · Cut off here, the way a search engine would. |
| 12 | Panel: slug edited | The Auto badge is gone. "Reset to match the title" brings it back. | Reset to match the title |
| 13 | Panel: slug taken | Checked on blur and on save. Nothing is saved until a free slug is chosen. | The slug "bathroom-costs" is already used on Renew. · Use bathroom-costs-2 |
| 14 | Panel: cover without alt | Alt text is required before the cover counts as ready. It is a suggestion, not an error. | Alt text · Needed before the cover counts as ready. Sent as image alt in the API. · Describe the image for people who can't see it · The cover image has no alt text. Add a short description of it. |
| 15 | Panel: choose cover | The shared Media picker. | Choose a cover image · The cover for this article. · Use image |
| 16 | Panel: social sharing open | Optional overrides. Each falls back to the meta values. | Social sharing · Optional · Social title · Using meta title · Social description · Using meta description · Social image · Using the cover image. |
| 17 | Panel: social customised | The card shows the overrides, so the Using chips disappear. | Customised |
| 18 | Panel: advanced, invalid canonical | Collapsed by default. The address must be absolute. | Advanced · Canonical URL · Only change this if the same content lives at another address. · Enter a full address starting with https:// |
| 19 | Panel: hidden from search | The switch only sets a flag in the API. | Hide from search engines · Sets noindex in the API. It doesn't change who can read the article. |
| 20 | Panel: mobile search preview | The Desktop and Mobile toggle changes the width and where the title is cut. | Search result · Desktop · Mobile |
| 21 | Panel: checking | Checks run on open and after each pause in typing. | Checking this article… |
| 22 | Panel: saving | Fields are disabled while the request runs. | Saving… |
| 23 | Panel: save failed | 500 or network failure. The values stay. | Couldn't save search and sharing. Your changes are still here. Try again in a moment. |
| 24 | Saved | The panel closes. The summary updates. | Search and sharing saved · Your search and sharing settings are up to date. |
Mobile: the section sits under "Site and authors" in the Details sheet. "Edit search and sharing" opens a 92% sheet on top of the Details sheet, with suggestions first, then fields, then the two previews. Publishing is allowed regardless of suggestions; Save and Publish are never disabled by them.
Editor: what the frontend gets (SEO round)
Route: /article/:id/edit. API: GET /v1/article/{id}/seo.
A read-only dialog (desktop, 720 px) or bottom sheet (mobile) with the resolved SEO object exactly as the API returns it. Opened from the sidebar section or the panel. Each value that came from a fallback carries a chip. Formatted shows a compact key and value list, Raw shows the JSON. Copy JSON copies the raw object. Author name, profile URL and social links come from the Authors screen and surface in authors[] and in the JSON-LD author, so Authors itself needs no change.
| # | State | Trigger | Copy |
|---|---|---|---|
| 1 | Formatted | Default view. | What the frontend gets · The SEO data returned by GET /v1/article/12/seo, read only. RenewCMS doesn't render pages or add meta tags: your frontend does. · Formatted · Raw JSON · Copy JSON · Close |
| 2 | Raw JSON | The JSON as the API returns it. | Raw JSON |
| 3 | Copied | The button reads Copied for 2 s and this is announced. | Copied |
| 4 | Draft article | Drafts answer 404 to anonymous callers. | This article is a draft. The API returns this data only to signed-in callers until it is published. |
| 5 | Loading | Copy is disabled. | – |
| 6 | Error | GET failed. Nothing in the article changes. | Couldn't load the SEO data · GET /v1/article/12/seo failed. Your article is unchanged. Try again in a moment. · Try again |