| 1 | # Shared save status |
| 2 | |
| 3 | Status: revision 2 approved by Claude Fable 5.1 (claude-fable-5-1), 2026-09-07. |
| 4 | Implementation and consumer adoption require branch review. Full initial and |
| 5 | revised verdicts are recorded in Amadan Estilo discussion 1. |
| 6 | User instruction: extract reusable components and maintain consistent suite UI/UX |
| 7 | through Estilo during the overnight implementation run. |
| 8 | |
| 9 | ## Concrete consumers |
| 10 | |
| 11 | Docs at 499d200 has a polite live region in views/show.html with two localized |
| 12 | messages (retrying, failed), styled as a fixed notice. Its editor sets data-state; |
| 13 | successful routine saves deliberately have no visible message. |
| 14 | |
| 15 | Slides slice-2-editor at a863fb6 has a status line in templates/editor.html and |
| 16 | editor.css. editor-entry.js updates its text and data-state. It displays saved, |
| 17 | saving, offline and error messages from the app's catalog. Existing session |
| 18 | owns unfinished editor work; adoption will use an isolated branch after its |
| 19 | current revision and be coordinated before merging. |
| 20 | |
| 21 | ## Primitive |
| 22 | |
| 23 | Build the first shared component in Estilo, consumed by Docs first; Slides is |
| 24 | the second adoption that validates/corrects the interface (AGENTS.md). |
| 25 | |
| 26 | Package savestatus exports an embedded html/template definition plus CSS. |
| 27 | Data is a struct with localized text for Saved, Saving, Retrying, Failed and |
| 28 | an optional localized accessible label. Missing text means that state stays |
| 29 | visually silent. No English default or authored product copy lives here. |
| 30 | The initial state is validated to a closed set, including idle. Unknown states |
| 31 | are silent and never represented as saved. |
| 32 | |
| 33 | Markup uses estilo-save-status and estilo-save-message attributes, role=status, |
| 34 | aria-live=polite, aria-atomic=true. Messages are ordinary template-escaped strings. |
| 35 | The component is non-interactive. State changes are reflected by a small shared |
| 36 | JavaScript function updating textContent in the already-present live region; |
| 37 | it never injects HTML. Without script, the initial status text is visible. |
| 38 | |
| 39 | Slides maps saved/saving directly. Its offline state maps to retrying and retains |
| 40 | the existing localized offline text. Its sticky dropped-batch case continues |
| 41 | showing that same offline text, never saved; no new failed message is invented. |
| 42 | Slide-load errors remain an app-owned element separate from save status, using |
| 43 | the existing localized error text. The adopter must not claim a successful save |
| 44 | clears a load error, or a successful load clears a dropped batch. |
| 45 | |
| 46 | Two placements are needed by the existing consumers: inline and floating. |
| 47 | CSS inherits rastrillo tokens, wraps long text, respects 320px reflow and uses |
| 48 | words plus weight for failure emphasis; it changes no theme value. App code |
| 49 | continues to own save lifecycle, sticky failure, retry and recovery semantics. |
| 50 | The floating failure retains Docs' negative background/foreground tone tokens. |
| 51 | Inline placement retains Slides' minimum inline size of 8ch to avoid jitter. |
| 52 | Both variants disappear for print via the component's own print rule. |
| 53 | |
| 54 | Silent status is an empty rendered live region: never display:none, visibility: |
| 55 | hidden, hidden, or aria-hidden. Empty floating state has no padding/background/ |
| 56 | shadow, but the element remains present before a subsequent text mutation. This |
| 57 | keeps the region in the accessibility tree before its first failure announcement. |
| 58 | |
| 59 | Do not ship a new all-green appearance for Docs routine saves, and do not |
| 60 | change failure/retry wording. Do not infer save state from HTTP activity alone. |
| 61 | |
| 62 | ## Integration |
| 63 | |
| 64 | Both app adopters import the same Estilo package revision and serve its CSS/JS |
| 65 | through their existing embedded asset route. Their templates use the shared |
| 66 | markup. Remove the replaced app-local status styling and DOM-writing functions. |
| 67 | Retain an app-specific selector only where needed during the same migration; |
| 68 | remove duplicate status DOM. No dynamic CDN imports, new framework, or bundler. |
| 69 | |
| 70 | Delivery is one same-origin ES module URL exposed in the editor's data attribute. |
| 71 | The bundle loads it with dynamic import of that URL (kept external to bundling) |
| 72 | and calls its exported mount function. The element holds template-escaped |
| 73 | data-text-saved/saving/retrying/failed values; the module reads those once. |
| 74 | Both apps' existing static routes serve the embedded module and CSS. Module-load |
| 75 | failure must leave existing error/retry indication operational through a minimal |
| 76 | app fallback or a pre-editor failure state; it must never silently run an editor |
| 77 | whose persistence failure cannot be shown. The implementation review must verify |
| 78 | this delivery/failure path in the actual bundle, not only the helper's unit test. |
| 79 | |
| 80 | ## Verification |
| 81 | |
| 82 | Test HTML escaping, invalid state handling, missing labels/messages, role/live |
| 83 | region and scriptless initial state. Exercise state transitions on a real DOM |
| 84 | and prove failure wording is not replaced by stale save completion through app |
| 85 | tests. Run Estilo theme/token-prefix tests unchanged and each consumer's gates. |
| 86 | Verify both variants at 320px and keyboard/screen-reader semantics. Review full |
| 87 | library and consumer diffs with Fable before CI/merge/deploy. |
| 88 | Exercise repeated saving/offline/failed transitions; the live region remains in |
| 89 | the DOM/accessibility tree while silent. Check Slides' sticky failure never |
| 90 | returns to saved after a dropped batch; check load-error independence. Measure |
| 91 | 320px using Docs' longest existing failed text and widest available locale. |
| 92 | |
| 93 | No extraction is called complete until two apps consume it and their old |
| 94 | duplicate implementation is removed. Until then it is an in-progress primitive. |
| 95 | |
| 96 | Fable review 1: claude-fable-5-1, /tmp/oficina-overnight-review/estilo.json. |
| 97 | All seven findings accepted above: exact Slides mapping, persistent live region, |
| 98 | explicit JS delivery, print, inline width, negative tone, pinned Slides revision. |
| 99 | |
| 100 | ## Implementation evidence |
| 101 | |
| 102 | The floating notice uses pointer-events:none so it cannot block the editor. |
| 103 | The full local make test gate passes. Mutation checks reject treating an unknown |
| 104 | state as saved and replacing textContent with innerHTML. |
| 105 | |
| 106 | ## Private module transport |
| 107 | |
| 108 | Estilo remains private. Use module path amadan.net/oficina/estilo.git, |
| 109 | GOPRIVATE=amadan.net and authenticated Git transport. Go documents the VCS |
| 110 | suffix as the supported way to resolve a private repository without public |
| 111 | go-import metadata. A fresh direct download of5f712c7 via that path resolved |
| 112 | the exact repository and commit. No public visibility change or local replace |
| 113 | is part of the shipping dependency. Both first consumers adopt the qualified |
| 114 | module path; no existing consumer pin was found. |
| 115 | |