Files
RenewCMS/docs/design/states.md
T

21 KiB
Raw Permalink Blame History

States

Every state in the three boards is drawn in light and dark (theme switch) at 1440×900 and 390×844. Counts are in README.md.

Clock-sensitive states

Only individual states that render a relative time or a countdown are marked "Clock-sensitive" on the board: Articles "Published" (Updated reads "Just now"), Media "Upload: too many requests (429)" and Settings "Too many requests (429)" (countdowns), Users "Renew invite: done", and the Settings update states that show "Last checked today at 09:41" or "yesterday" (Up to date, Check failed, Update available, Update available with notes open, Update done, Plain restart: confirm, Admin view, Role changed, Update already running). Capture and test them with a frozen clock at 13 Oct 2026, 09:41. Frames that merely list dated rows are not marked.

Sites and authors: roles

Permission model: access follows the role's permissions (Manage sites, Manage authors). The built-in Owner and Admin roles hold them, so the copy below still says "owner" and "owner or admin", as the app ships it. Moving the copy to permission wording is a separate app change (proposal).

Sites: only the owner can create, edit, delete or set the default; other roles have no Sites entry and get a 403 page on a direct link. Anyone who can edit an article can set its site and read the site list. Authors: owner and admin manage; other roles see the list and detail read-only and can create and edit their own author only. The site key is immutable after creation.

Copy glossary

Use Not
Delete (removes a record: article, variant, image, invite, cache files) Remove
Remove link, Remove from the article, Remove (detaches; the record stays) Delete
Create, Add variant, Create draft New (except the page and button name "New article")
Save (a form or the editor), Save changes (edit forms) Update (Update is the software-update action only)
Cancel on the left, primary on the right Close on a dialog that has an action
Close only when the dialog has no action Done, OK
Explicit non-destructive label in place of Cancel when Cancel would be ambiguous: "Keep invite", "Keep key", "Restart later" Cancel there
Sentence case everywhere Title Case
Errors say what happened and what was kept, end with a full stop Exclamation marks
Toast: title is a short result in the past tense ("Article deleted", "Download started"), body one sentence with a full stop Titles with a full stop; future tense
In-flight verb ends in an ellipsis character: "Saving…", "Creating…", "Preparing download…" Three dots

Glossary pass 2, changes made: activity log lines "Removed N expired invites" and "Removed N generated files" became "Deleted …" (they delete records or files); Activity export footer "Close" next to "Download again" became a single "Close"; export title "Export ready" became "Download started"; export failure title "Couldn't prepare the export" became "Couldn't prepare the download"; "Cancel export" became "Cancel". Checked by search across all frames and boards for exclamation marks (none found), Remove and Delete, Update and Save, "New", and dialog footers. Not re-read line by line; see README "Not done".

HTTP status to copy

Status Copy
400 "That request wasn't valid. Check the fields and try again."
401, 403 Global redirect, no local copy (403 from an unverified account). Role 403s have local copy: see the rows below
404 "That item no longer exists."
409 "This changed somewhere else. Reload and try again."
413 "That file is too large."
429 "Too many requests. Try again in a few minutes."
500, network "Something went wrong on our side. Nothing was changed. Try again in a moment."
400 invalid_site_url "Use https. For example https://blog.example.com." / "Remove the path. Use only the host, like https://blog.example.com." / "Enter a full URL, like https://blog.example.com." (inline under base url)
409 site_key_taken "A site with the key "journal" already exists. Choose another key." (inline under key, with a suggestion)
409 site_has_articles "Choose where the articles go. This site has 38 articles." (inside the delete dialog)
403 owner_only (page; raised when the role lacks Manage sites) "You don't have access to Sites" / "Only the owner can manage sites. Ask the owner of this site if you need a change." / "Back to Settings" (403 page on /settings/sites)
403 owner_only (action) "Only the owner can manage sites" / "Ask the owner of this site." (error toast, role changed while the page was open)
403 author_rights "Only an owner or admin can change authors" / "You can still edit your own author profile." (error toast, role changed while the page was open)
400 key_immutable "Used in API paths, so it can't change." (not shown: the edit form has no editable key)
400 invalid_locale "Use a language tag like en or fr-CH." (inline under locale)
403 default_site "The default site can't be deleted. Make another site the default first." (toast if the menu was stale)
404 site_not_found "That site no longer exists" / "It was deleted somewhere else, so we removed it from the list." (info toast)
409 author_slug_taken "The slug "camille-roche" is already used by another author." (inline under slug, with a suggestion)
409 author_has_articles "Can't delete this author" / ""Camille Roche" is credited on 3 articles. Choose another author to credit instead, or remove Camille Roche from those articles first." (server fallback dialog, drawn once)
409 user_already_linked "That user already has an author. Choose another user." (inline under linked user)
422 bio_too_long "Shorten the bio to 280 characters." (inline under bio)
422 invalid_url "Enter a full URL starting with https://." (inline under website and each social link)
404 author_not_found "That author no longer exists" / "It was deleted somewhere else, so we removed it from the list." (info toast)
404 site_not_found (sitemap) GET /v1/site/{key}/sitemap, unknown key. The body is the code site_not_found, shown on Integration in the code style (not rewritten as prose).
400 (sitemap page or size) "The request cannot be processed due to a mismatch in the format of the body." (same text as the other routes on Integration)
403 owner_only vs article SEO Integration note under the 403 row: "Site SEO defaults are owner only. Article SEO fields need only the right to edit the article."
500 – "Couldn't save the site. Your changes are still here. Try again in a moment." / "Couldn't save the author. Your changes are still here. Try again in a moment." / "Couldn't create the author" / "The article is unchanged. Try again in a moment." (notice inside the panel or dialog)
404 content_type_not_found Page: "Content type not found · It may have been deleted, or the address may be mistyped."
404 entry_not_found Page: "Entry not found · It may have been deleted by someone else."
409 content_type_key_taken Under the key: "The key "events" is already used by another content type. Choose a different key."
409 content_type_has_entries Delete dialog: "Recipes now has entries · Someone added an entry after this page loaded, so nothing was deleted. Delete the entries first, then try again."
400 invalid_content_type_key Under the key: "Use 2 to 40 lowercase letters, digits or hyphens."
400 invalid_field On the marked field, per rule (label, key, limits, default, choices). If the client missed one: "Check the fields marked in the list."
409 schema_incompatible Refusal dialog: "Nothing was changed · RenewCMS refused the changes because some entries don't fit the new fields. It doesn't say which entries."
422 invalid_entry: key: required "Fill in Title." (date: "Pick a date for Starts on.", choice: "Choose an option for Category.")
422 invalid_entry: key: type "Title must be text." / "must be a number." / "must be yes or no." / "must be a date, like 2026-11-21."
422 invalid_entry: key: min, max Text: "needs at least N characters", "can be up to N characters". Number: "must be at least N", "must be at most N". Date: "can't be before D", "can't be after D"
422 invalid_entry: key: options "Choose one of: Talk, Workshop or Meetup."
409 invalid_entry: key: unique "Another entry already uses this Title. Enter a different one." plus the alert "Not saved"
422 invalid_entry: key: unknown_field Alert "The fields of Events changed", button Reload form

Public image 400 bodies

{ "status": 400, "error": "unknown_variant", "message": "Unknown variant", "detail": "hero" } and { "status": 400, "error": "unsupported_parameter", "message": "Unsupported parameter", "detail": "width" }. The original is not served as a fallback.

Articles and Media pagination

Same states and copy on Articles and Media (Media draws them under the grid or list). Loading next page keeps row height, controls disabled, summary "Loading page 7…". Error "Couldn't load page 7. Showing page 6." with Retry. Out of range "That page doesn't exist. Showing the last page." Page size menu offers 5, 25, 50, 100. Invalid page or size in the URL (size=30, page=abc, page=0) is replaced by the default without a notice; the API 400 never reaches the user.

Sites and authors pagination

Groups "Sites: pagination" and "Authors: pagination", 10 states each, same pager as Articles and Media, sizes 5, 25, 50, 100 (default 25). First, middle and last page; single page (count line "N sites" or "N authors" only, no pager, no size select); page-size menu; loading ("Loading page N…", skeleton rows, pager kept and disabled); error ("Couldn't load page N. Showing page M." with Retry, rows and page number kept); empty after search (existing empty state, no pager); search resets to page 1; long labels on mobile. Out of range: ?page past the end is replaced by the last page, with "That page doesn't exist. Showing the last page." (not drawn; as Articles). Details in screens/sites.md and screens/authors.md.

Editor

Counter without limit ("1,204 words · 7,318 characters"; at 120,000 characters "19,845 words · 120,000 characters"), "Saving…", "Saved", "Couldn't save. Your text is kept. Retry". Link states and the toolbar overflow are drawn in the Editor group (11 states).

Activity export

Only CSV is offered. "Preparing download…", "Download started", "Couldn't prepare the download", "Nothing to export", "You can't export all activity". The CSV/JSON format control is drawn once, labelled proposed.

Media cleanup, Settings navigation, Activity toggles

As in screens/settings.md, screens/image-variants.md and screens/activity.md. Sync rule for filter cards: card pressed, chip present and the Outcome segment on Failed are one state; removing any removes all three.

Per-screen copy

screens/.md, section "Copy per state" (generated from the board source).

SEO round

Frames: Editor: search and sharing (24), Editor: what the frontend gets (6), Sites: SEO defaults (5), Articles: search status (3). Each is drawn desktop and mobile; light and dark follow the theme switch. None is clock-sensitive: dates in the API preview are fixed sample values. Disabled state: all fields at 40% while saving. Validation: slug taken, canonical not absolute, title template without %title%, excerpt over 300. Suggestions never block saving or publishing. Copy per state: screens/editor.md, articles.md, sites.md.

Custom content types

Groups on RenewCMS Screens Schema.dc.html: 1 Schema list (6), 2 Schema builder (16), 3 Review and delete (7), 4 Field types (6), 5 Content and entry list (9), 6 Entry editor (13), 7 Relations (4), 8 Permissions (2), 9 Export and import (3). 66 states, 56 available now and 10 later. Each has a 1440 and a 390 frame; light and dark follow the theme switch. Nothing is clock-sensitive. Tag per state: "Available now" or "Later". Copy per state: screens/schema.md, content.md, entry.md, schema-later.md.

Roles: owners and admins see Schema and edit types. Any verified user sees the Content group and reads and writes entries. A user who is not owner or admin and opens /schema gets "You don't have access to Schema".

Keys: a type key is 2 to 40 lowercase letters, digits and hyphens (chosen at creation, never changes). A field key is lowercase letters, digits and underscores, starts with a letter, up to 40 (editable until its field is first saved). The two inputs show their own rule.

Schema changes with entries: Save opens the review ("Add 2 fields, change 1, remove 1"); Apply sends the PUT; schema_incompatible shows "Nothing was changed" and names the kind of change that likely caused it, never an entry. A type with entries cannot be deleted ("You can't delete Events yet").

Entry rules, 422 except unique (409): required, type, min, max, options, unique, unknown_field. Messages are per type and drawn in the field-type gallery (group "Field types").

Media field (pass 5)

Copy and states are in media-field.md (sections 2 to 6). Entry rules for a media field: required 422 "Pick an image for Thumbnail."; type 422 "Thumbnail must be an image from the library."; min, max, options and unique do not apply. A required media field added to a type with entries is refused client side in the review ("Can't be applied") and by the server as 409 schema_incompatible. Image deleted while entries use it: the control shows "This image is no longer in the library"; saving is refused only when the field is required. HTTP statuses for the picker and the field use the table above (400, 401, 403, 404, 409, 413, 429, 500, network).

Roles and permissions

90 states, all Available now, listed with trigger and copy in screens/roles.md. Groups: Roles list 7, Role editor 15, Plain-language preview 4, Delete role 4, Users 9, Role-aware shell 8, No access 3, Activity 5, Profile 3, API keys 1, Create-a-role flow 6, Toasts 6, Inline errors 9, Read = Own 5, 409 conflicts 5. None is clock-sensitive (times are fixed strings).

Hidden or disabled:

Case Treatment Example
Whole area the role never has Hidden (no sidebar item, no page card). Direct link shows the 403 page. Schema for a Writer
Action on something visible, blocked by role Disabled with a reason in a tooltip or row text Delete on an article for a Writer
Action that only exists above their level Hidden Publish for a Writer; status shows Draft and "Ask an editor to publish."
Row the person can read but not edit (Own) View only mark on the row; Edit and Delete disabled in the menu You can only edit content you created.
Settings page they can read Read-only: locked fields, banner, Save off Admin on General without Edit general settings
Owner-only action visible to an admin Disabled, Owner only tag Apply update

No-escalation feedback:

Rule Where it shows Copy
Grant only what you hold Disabled segment or switch with a lock; popover on focus or hover You can only grant what your role has. Your role can publish only entries you created, so All isn't available.
No role above yours Disabled option in the role picker (Change role, Delete reassign, Invite) Your role can't assign a role with more power than yours.
No editing your own role Editor opens read-only with a banner You can't change the permissions of your own role. Ask another owner or admin.
No delete while it has members Delete dialog, blocked state 4 members have this role. Move them to another role first. Then Contributor can be deleted.
Owner-only list Disabled item with an Owner only tag and a lock Only the owner can apply updates.

HTTP status to copy (additions):

Code Status Title Body
missing_permission 403 You don't have permission to do that Your role doesn't include {permission name}. Ask an owner or admin.
role_locked 403 This role is locked The Owner role can't be changed or deleted. Transfer ownership from Users instead.
role_has_members 409 Role still has members {n} members have this role. Move them to another role first.
role_name_taken 409 Name already used A role named "{name}" already exists. Choose a different name.
role_conflict 409 This role changed while you were editing {name} saved changes to {role} at {time}. Reload to see them, or review how they differ from your edits.
escalation 403 You can't grant that You can only grant what your role has.
(page level) 403 You don't have access to this page Your role, {role}, doesn't include it. Ask an owner or admin if you need it.

Roles sync (pass 6)

Wording

People are referred to as "they" everywhere: "Their API keys follow the new role." "You can't take ownership back unless they transfer it." No "he", "she", "his" or "her" in any frame or copy table. French uses "vous"; possessives agree with the noun, so they stay neutral ("ses clés API"). Built-in role names stay in English in the French copy until the translation of Owner, Admin and Writer is decided; custom names are user data and are never translated.

Activity event names

Present tense, as the backend emits them. The outcome (succeeded or failed) is a field, so a failed event keeps its name.

Event Sentence Failed sentence
role.create Role Contributor created Failed to create the role Contributor
role.edit Role Contributor edited Failed to edit the role Contributor
role.delete Role Intern deleted Failed to delete the role Intern
role.reassign Moved 3 members from Intern to Writer Failed to move 3 members from Intern to Writer
user.change_role Ines Duarte's role changed from Writer to Content lead Failed to change the role of Ines Duarte
user.transfer_ownership Ownership transferred from Marta Alves to Jonas Weber Failed to transfer ownership to Jonas Weber
Failed template: "Failed to {verb} {object}". Category names in the filter: Articles, Media, Users and invites, Roles, API keys, Authentication, System. French for every string is in the board table "Final copy: toasts and errors, EN and FR" and in screens/roles.md.

Stale state

Nothing is changed when a request is stale. The message names who or what changed, says nothing was changed, and ends with the recovery. Where it shows: banner in the role editor, alert inside a dialog, toast only when the surface is no longer open. The form keeps what the person entered. Recovery: Reload (toast), Reload role or Review differences (editor), Reload member (transfer), retry (change role).

Case Status Where Copy
Stale role save 409 role_conflict Banner in the editor This role changed while you were editing · {name} saved changes to {role} at {time}. Reload to see them, or review how they differ from your edits.
Change of role, member's role changed first 409 Alert in the dialog, or toast if it closed Role changed in the meantime · Ines Duarte is now Admin. Your choice is kept. Check it and try again.
Transfer, target's role changed first 409 Alert in the dialog, or toast if it closed Role changed in the meantime · Jonas Weber is now Writer. What you typed is kept. Check the change, then try again.
Removal, member's role changed first 409 Toast User not removed · Ines Duarte's role changed in the meantime, so nothing was removed. Check it and try again.
Role deleted by someone else 404 Info toast That role no longer exists · It was deleted somewhere else, so we removed it from the list.

Read = Own

A role with Read set to Own sees online articles plus its own drafts. For custom content types it sees only entries it created. Counts, totals and pagination follow the same rule, so hidden items are not hinted at. Update or Delete set to Own still list other people's rows with the View only mark. Frames: group "14 Read = Own" on the Roles board.

Wording status

Strings that match messages.ts are verbatim. "Only the owner can manage sites…" and other board-only wording are proposals. Moving the app to permission-based wording is a separate code change.

Account: change email, export, delete

States and copy in screens/account.md. Board: RenewCMS Screens Account.dc.html (33 states) and the Auth board groups "Verify new email" (7) and "Account deleted" (1). Direct-download rate limit state is clock-sensitive (frozen clock 09:41).

Status Copy
403 wrong_password "That password isn't correct. Try again." (inline under password)
429 existing 429 copy, inline in the form or the dialog
500, network existing 500 copy, or "Couldn't delete your account. Nothing was deleted. Try again in a moment."
409 last_admin "You can't delete your account yet" (non-dismissable dialog)
409 email_unavailable Landing only: "We couldn't change your email address" (same for in use and rejected)