oficina / estilo Public

plan: share save status through Estilo with two consumers

Paul Campbell pushed by paul@keymail.dev 0cd800f704fd6b066fc57ffb6409f5bfc039c469 parent ad0edf4
1 file changed, +96 −0
  • implementation/save-status.md +96 −0

CI cancelled — superseded by a newer push — run details

Intent

Attributed to this commit: 17.5 g CO₂e (459564 tokens). This is an attribution, not a measurement — usage counted from this commit's own marker forward to the next one on feat/shared-save-status, never backward from it. A turn that produced more than one commit books all of its tokens to the last one, and a concurrent session on this branch can add to the same figure. Some of it was priced using the shared default rate, because no model-specific factor row exists yet for at least one model involved. The Carbon view has the repo total, which is never derived from this attribution.

diff --git a/implementation/save-status.md b/implementation/save-status.md
new file mode 100644
index 0000000..93916ec
--- /dev/null
+++ b/implementation/save-status.md
@@ -0,0 +1,96 @@
+# Shared save status
+
+Status: revision 2, answering Fable review 1; re-review pending, 2026-09-07.
+User instruction: extract reusable components and maintain consistent suite UI/UX
+through Estilo during the overnight implementation run.
+
+## Concrete consumers
+
+Docs at 499d200 has a polite live region in views/show.html with two localized
+messages (retrying, failed), styled as a fixed notice. Its editor sets data-state;
+successful routine saves deliberately have no visible message.
+
+Slides slice-2-editor at a863fb6 has a status line in templates/editor.html and
+editor.css. editor-entry.js updates its text and data-state. It displays saved,
+saving, offline and error messages from the app's catalog. Existing session
+owns unfinished editor work; adoption will use an isolated branch after its
+current revision and be coordinated before merging.
+
+## Primitive
+
+Build the first shared component in Estilo, consumed by Docs first; Slides is
+the second adoption that validates/corrects the interface (AGENTS.md).
+
+Package savestatus exports an embedded html/template definition plus CSS.
+Data is a struct with localized text for Saved, Saving, Retrying, Failed and
+an optional localized accessible label. Missing text means that state stays
+visually silent. No English default or authored product copy lives here.
+The initial state is validated to a closed set, including idle. Unknown states
+are silent and never represented as saved.
+
+Markup uses estilo-save-status and estilo-save-message attributes, role=status,
+aria-live=polite, aria-atomic=true. Messages are ordinary template-escaped strings.
+The component is non-interactive. State changes are reflected by a small shared
+JavaScript function updating textContent in the already-present live region;
+it never injects HTML. Without script, the initial status text is visible.
+
+Slides maps saved/saving directly. Its offline state maps to retrying and retains
+the existing localized offline text. Its sticky dropped-batch case continues
+showing that same offline text, never saved; no new failed message is invented.
+Slide-load errors remain an app-owned element separate from save status, using
+the existing localized error text. The adopter must not claim a successful save
+clears a load error, or a successful load clears a dropped batch.
+
+Two placements are needed by the existing consumers: inline and floating.
+CSS inherits rastrillo tokens, wraps long text, respects 320px reflow and uses
+words plus weight for failure emphasis; it changes no theme value. App code
+continues to own save lifecycle, sticky failure, retry and recovery semantics.
+The floating failure retains Docs' negative background/foreground tone tokens.
+Inline placement retains Slides' minimum inline size of 8ch to avoid jitter.
+Both variants disappear for print via the component's own print rule.
+
+Silent status is an empty rendered live region: never display:none, visibility:
+hidden, hidden, or aria-hidden. Empty floating state has no padding/background/
+shadow, but the element remains present before a subsequent text mutation. This
+keeps the region in the accessibility tree before its first failure announcement.
+
+Do not ship a new all-green appearance for Docs routine saves, and do not
+change failure/retry wording. Do not infer save state from HTTP activity alone.
+
+## Integration
+
+Both app adopters import the same Estilo package revision and serve its CSS/JS
+through their existing embedded asset route. Their templates use the shared
+markup. Remove the replaced app-local status styling and DOM-writing functions.
+Retain an app-specific selector only where needed during the same migration;
+remove duplicate status DOM. No dynamic CDN imports, new framework, or bundler.
+
+Delivery is one same-origin ES module URL exposed in the editor's data attribute.
+The bundle loads it with dynamic import of that URL (kept external to bundling)
+and calls its exported mount function. The element holds template-escaped
+data-text-saved/saving/retrying/failed values; the module reads those once.
+Both apps' existing static routes serve the embedded module and CSS. Module-load
+failure must leave existing error/retry indication operational through a minimal
+app fallback or a pre-editor failure state; it must never silently run an editor
+whose persistence failure cannot be shown. The implementation review must verify
+this delivery/failure path in the actual bundle, not only the helper's unit test.
+
+## Verification
+
+Test HTML escaping, invalid state handling, missing labels/messages, role/live
+region and scriptless initial state. Exercise state transitions on a real DOM
+and prove failure wording is not replaced by stale save completion through app
+tests. Run Estilo theme/token-prefix tests unchanged and each consumer's gates.
+Verify both variants at 320px and keyboard/screen-reader semantics. Review full
+library and consumer diffs with Fable before CI/merge/deploy.
+Exercise repeated saving/offline/failed transitions; the live region remains in
+the DOM/accessibility tree while silent. Check Slides' sticky failure never
+returns to saved after a dropped batch; check load-error independence. Measure
+320px using Docs' longest existing failed text and widest available locale.
+
+No extraction is called complete until two apps consume it and their old
+duplicate implementation is removed. Until then it is an in-progress primitive.
+
+Fable review 1: claude-fable-5-1, /tmp/oficina-overnight-review/estilo.json.
+All seven findings accepted above: exact Slides mapping, persistent live region,
+explicit JS delivery, print, inline width, negative tone, pinned Slides revision.