`, numbered lines to ``, bullets to ``, rest to ``
4. Full-document single scroll (NOT paginated) — see question 4 for rationale
This is readable, navigable by AT users, and can be produced entirely from the stored `pages_text` metadata without any additional Pdfium calls.
---
## 3. Signing Form Complication
### The core problem
In the signing form (`submit_form/show.html.erb`), the sticky bottom panel contains the Vue 3 submission form component that drives the signing workflow — field navigation, signature capture, completion. This Vue component reads the DOM for `page-container` elements to drive its scroll-to-field logic. A tab switch that replaces the PDF panel with a text panel would break the Vue component's DOM assumptions.
Additionally, in the signing context, the legally relevant representation is the PDF image. The signer is attesting to the document as visually presented. Replacing that with extracted text in the signing flow creates the same trust problem identified in the previous expert opinion.
### Evaluation of options
#### Option A: Text View is read-only; switch back to PDF View to sign
**UX model**: Two-mode. User can read in Text View, then explicitly return to PDF View to complete fields.
**Accessibility impact**: For AT users (screen reader, keyboard-only), this is workable. The tab switch is a clear, explicit action. However, it creates a redundant navigation burden: AT user reads text in Text View, must switch back to PDF View, must re-navigate to the first incomplete field.
**Implementation complexity**: Low. Text panel has no form fields. Vue component remains in PDF panel and is not affected.
**Verdict**: Acceptable for MVP. The return-to-sign burden is real but not severe for a two-page NDA. For a 40-page complex form, it is a problem.
#### Option B: Inline form fields in Text View
**What it would require**: Mapping each form field's page/coordinates to a position in the heuristic-parsed text, then inserting Vue field components at those positions. This requires coordinate-to-text-position mapping that the current data model does not support — you would need to store character bounding boxes (Approach D data) and match field bounding boxes (from `fields_index` areas) to character positions.
**Verdict**: Reject for MVP. Engineering effort is 2-3 weeks minimum. The data infrastructure does not exist. This is a future Phase 3 investment if the product team commits to it.
#### Option C: Text View with sticky "Continue Signing" CTA
**What it is**: Text View shows the document text. At the bottom (sticky) is a persistent call-to-action: "Return to PDF View to complete signing" that scrolls or switches to the PDF tab.
**Accessibility impact**: This is good. AT users can read the full document without losing context, then explicitly navigate to the completion action. The CTA is focusable, labeled, and persistent.
**Implementation complexity**: Low. The sticky CTA is just a button in the Text panel that calls the tab-switch function and scrolls to the first incomplete field.
**Verdict**: This is the right choice for the signing form MVP. Minimal engineering, clear user model, accessible.
#### Option D: Text View as a drawer/panel, not a full tab replace
**What it is**: Text View does not replace the PDF panel. Instead, a side panel or overlay drawer shows the text alongside the PDF. On mobile, it would be a bottom sheet.
**Problem in signing context**: The signing form already has a fixed bottom panel (the Vue submission form). A text drawer would compete for vertical space on mobile.
**Problem in general**: The `submissions/show.html.erb` already has a three-column layout (document thumbnail sidebar + document view + parties sidebar). Adding a fourth pane is not feasible.
**Verdict**: Reject. The existing layout does not have room for a persistent text panel alongside the PDF on the signing form. On the submission preview, the right panel (parties view) already serves a different purpose.
### Recommendation for signing form
Use **Option A + Option C combined**:
- Text View in the signing form is read-only
- A sticky non-scrolling CTA banner at the bottom of the Text panel (inside the tabpanel, positioned sticky within it) says "Ready to sign? Switch back to PDF View" with a button that activates the PDF tab
- The Vue submission form panel (at the page level, outside the tabpanel) is unaffected and remains visible at all times (in both tab states) so the user always knows signing is available
This means the signing form's tab behavior is:
- **PDF View tab active**: Normal signing experience, Vue form panel at bottom
- **Text View tab active**: Text content, Vue form panel still at bottom (always visible), text panel has a banner pointing back to PDF View
The Vue form panel being always-visible in both tab states means switching to Text View does not break the signing workflow — it just replaces the document image area with the text area, while the signing controls remain accessible.
---
## 4. Scoped Recommendation
### Right scope: both pages, but different behavior
**Submission preview (`submissions/show.html.erb`)**: Full "PDF View / Text View" tab switcher. Text View is read-only, full-document single scroll. This is the simplest case — no signing complications. Prioritize this page first.
**Signing form (`submit_form/show.html.erb`)**: Tab switcher with read-only Text View and sticky "return to sign" CTA. The Vue submission form panel remains visible in both views. Implement second, after preview is stable.
**Template builder**: Do not add a Text View. The builder is for document authors who need to see the visual layout for field placement. Text View is not relevant to their task.
### Minimum implementation that delivers real value
The users who benefit most are:
1. **Low-vision users not using screen readers** — they can zoom text, use browser translate, use dyslexia fonts via browser extensions. All require visible DOM text.
2. **Cognitive disability users** — simplified, reflowed text without visual PDF complexity reduces cognitive load when reading a contract before signing.
3. **Language barrier users** — browser auto-translate works on visible DOM text. A French speaker receiving an English NDA can press translate and read a machine-translated version before signing.
4. **Mobile users on slow connections** — text loads instantly, images may not. Text View as a fallback for poor network conditions is a real practical benefit.
None of these users are served by the current `sr-only` implementation. The tab switcher is the correct minimal feature to serve them.
### Per-page vs. full-document single scroll
**Single scroll is strongly preferred.** Here is why:
1. **AT navigation**: Screen reader users navigate long text by headings (H key in JAWS/NVDA). A full-document single article with heading structure lets them jump to "Section 3" of a contract without pagination friction. Per-page tabs or pagination destroys this.
2. **Browser Find in Page**: Works across the entire visible document. If content is paged, Cmd+F only searches the visible page. A user searching for "indemnification" would not find it on page 4 if they are viewing page 1.
3. **Browser translate**: Chrome's Page Translate works on the visible DOM. A paged text view may not translate the full document in one pass.
4. **Copy/paste**: Users who want to copy a clause from a multi-page document do not want to page through it; they want to Cmd+A or select-and-copy across a continuous document.
5. **Cognitive load**: Pagination introduces navigation overhead. Users with cognitive disabilities benefit from fewer controls, not more.
**Single scroll with per-page section headings** gives the best of both worlds: the document reads as a continuous flow (good for linear reading), but AT users can jump to "Page 3" section marker (good for navigation) and sighted users can scroll normally.
### Pitfalls the team might miss
#### 1. The tab switcher must remember state across Turbo navigation
DocuSeal uses Turbo Drive. If the user switches to Text View and then Turbo navigates away and back, the tab state resets to PDF View. This is probably acceptable behavior (default to PDF on fresh page load), but it should be deliberate. Store the preference in `localStorage` and restore it on `turbo:load`. Do not use a cookie (requires server round-trip, affects legal audit trail unnecessarily).
#### 2. The hidden panel must use `hidden` attribute or `display: none`, not `visibility: hidden`
`visibility: hidden` keeps the element in the accessibility tree. AT users would navigate into a "hidden" panel. Use `hidden` HTML attribute (maps to `display: none`) or `aria-hidden="true"` on inactive panels. The ARIA tab pattern requires that inactive `tabpanel` elements use `hidden` attribute (not just visual hiding with CSS classes).
DaisyUI's tab component may not do this correctly out of the box — verify before shipping.
#### 3. Focus management on tab switch
When the user clicks/keyboards to a new tab, focus should remain on the newly activated tab button — NOT move into the panel content. The panel becomes accessible by pressing Tab from the active tab. Do not auto-focus the panel on activation.
Exception: if the panel was activated via keyboard and the panel has no focusable children, Tab after activating the tab should move into the panel. This is standard APG behavior.
#### 4. Text quality disclosure
Somewhere in the Text View — either a brief banner or a tooltip on the tab button — inform users that "Text View provides an accessible alternative. The PDF View is the authoritative document." This is not a legal disclaimer (that is overkill) but a brief user-facing note that sets correct expectations. Example: `
This text representation is provided for accessibility. The PDF view is the signed document.
` at the top of the text panel.
#### 5. The 15-page extraction cap
`MAX_NUMBER_OF_PAGES_PROCESSED = 15` means documents with more than 15 pages will have no text for pages 16+. The Text View must handle this gracefully. Options:
- Show text for pages 1-15, then an "i" info message: "Text not available for pages 16 and beyond"
- Do not show the Text View tab at all if the document exceeds 15 pages (simpler, avoids partial text confusion)
The second option is more conservative and avoids the misleading "there is text but only for some pages" situation. Recommendation: hide the tab if `pages_text` keys count < `number_of_pages`. This is a simple Ruby check.
#### 6. RTL document handling
The `dir="auto"` attribute on paragraph elements is essential for documents that mix Hebrew, Arabic, or Persian text (which DocuSeal's multilingual user base may encounter). `` lets the browser infer text direction per-paragraph. Without it, RTL text in an LTR container renders as reversed word-soup.
#### 7. Do not use `role="tablist"` + DaisyUI checkbox tabs
DaisyUI's tab pattern uses `` and CSS `:checked` pseudo-selectors, not the ARIA tab pattern. This is a completely different interaction model and does NOT satisfy APG keyboard behavior. If using DaisyUI, you must either:
a. Override DaisyUI tabs with a JavaScript-driven ARIA tab implementation, or
b. Use the `
` + `
- `, rest to `
` 4. Full-document single scroll (NOT paginated) — see question 4 for rationale This is readable, navigable by AT users, and can be produced entirely from the stored `pages_text` metadata without any additional Pdfium calls. --- ## 3. Signing Form Complication ### The core problem In the signing form (`submit_form/show.html.erb`), the sticky bottom panel contains the Vue 3 submission form component that drives the signing workflow — field navigation, signature capture, completion. This Vue component reads the DOM for `page-container` elements to drive its scroll-to-field logic. A tab switch that replaces the PDF panel with a text panel would break the Vue component's DOM assumptions. Additionally, in the signing context, the legally relevant representation is the PDF image. The signer is attesting to the document as visually presented. Replacing that with extracted text in the signing flow creates the same trust problem identified in the previous expert opinion. ### Evaluation of options #### Option A: Text View is read-only; switch back to PDF View to sign **UX model**: Two-mode. User can read in Text View, then explicitly return to PDF View to complete fields. **Accessibility impact**: For AT users (screen reader, keyboard-only), this is workable. The tab switch is a clear, explicit action. However, it creates a redundant navigation burden: AT user reads text in Text View, must switch back to PDF View, must re-navigate to the first incomplete field. **Implementation complexity**: Low. Text panel has no form fields. Vue component remains in PDF panel and is not affected. **Verdict**: Acceptable for MVP. The return-to-sign burden is real but not severe for a two-page NDA. For a 40-page complex form, it is a problem. #### Option B: Inline form fields in Text View **What it would require**: Mapping each form field's page/coordinates to a position in the heuristic-parsed text, then inserting Vue field components at those positions. This requires coordinate-to-text-position mapping that the current data model does not support — you would need to store character bounding boxes (Approach D data) and match field bounding boxes (from `fields_index` areas) to character positions. **Verdict**: Reject for MVP. Engineering effort is 2-3 weeks minimum. The data infrastructure does not exist. This is a future Phase 3 investment if the product team commits to it. #### Option C: Text View with sticky "Continue Signing" CTA **What it is**: Text View shows the document text. At the bottom (sticky) is a persistent call-to-action: "Return to PDF View to complete signing" that scrolls or switches to the PDF tab. **Accessibility impact**: This is good. AT users can read the full document without losing context, then explicitly navigate to the completion action. The CTA is focusable, labeled, and persistent. **Implementation complexity**: Low. The sticky CTA is just a button in the Text panel that calls the tab-switch function and scrolls to the first incomplete field. **Verdict**: This is the right choice for the signing form MVP. Minimal engineering, clear user model, accessible. #### Option D: Text View as a drawer/panel, not a full tab replace **What it is**: Text View does not replace the PDF panel. Instead, a side panel or overlay drawer shows the text alongside the PDF. On mobile, it would be a bottom sheet. **Problem in signing context**: The signing form already has a fixed bottom panel (the Vue submission form). A text drawer would compete for vertical space on mobile. **Problem in general**: The `submissions/show.html.erb` already has a three-column layout (document thumbnail sidebar + document view + parties sidebar). Adding a fourth pane is not feasible. **Verdict**: Reject. The existing layout does not have room for a persistent text panel alongside the PDF on the signing form. On the submission preview, the right panel (parties view) already serves a different purpose. ### Recommendation for signing form Use **Option A + Option C combined**: - Text View in the signing form is read-only - A sticky non-scrolling CTA banner at the bottom of the Text panel (inside the tabpanel, positioned sticky within it) says "Ready to sign? Switch back to PDF View" with a button that activates the PDF tab - The Vue submission form panel (at the page level, outside the tabpanel) is unaffected and remains visible at all times (in both tab states) so the user always knows signing is available This means the signing form's tab behavior is: - **PDF View tab active**: Normal signing experience, Vue form panel at bottom - **Text View tab active**: Text content, Vue form panel still at bottom (always visible), text panel has a banner pointing back to PDF View The Vue form panel being always-visible in both tab states means switching to Text View does not break the signing workflow — it just replaces the document image area with the text area, while the signing controls remain accessible. --- ## 4. Scoped Recommendation ### Right scope: both pages, but different behavior **Submission preview (`submissions/show.html.erb`)**: Full "PDF View / Text View" tab switcher. Text View is read-only, full-document single scroll. This is the simplest case — no signing complications. Prioritize this page first. **Signing form (`submit_form/show.html.erb`)**: Tab switcher with read-only Text View and sticky "return to sign" CTA. The Vue submission form panel remains visible in both views. Implement second, after preview is stable. **Template builder**: Do not add a Text View. The builder is for document authors who need to see the visual layout for field placement. Text View is not relevant to their task. ### Minimum implementation that delivers real value The users who benefit most are: 1. **Low-vision users not using screen readers** — they can zoom text, use browser translate, use dyslexia fonts via browser extensions. All require visible DOM text. 2. **Cognitive disability users** — simplified, reflowed text without visual PDF complexity reduces cognitive load when reading a contract before signing. 3. **Language barrier users** — browser auto-translate works on visible DOM text. A French speaker receiving an English NDA can press translate and read a machine-translated version before signing. 4. **Mobile users on slow connections** — text loads instantly, images may not. Text View as a fallback for poor network conditions is a real practical benefit. None of these users are served by the current `sr-only` implementation. The tab switcher is the correct minimal feature to serve them. ### Per-page vs. full-document single scroll **Single scroll is strongly preferred.** Here is why: 1. **AT navigation**: Screen reader users navigate long text by headings (H key in JAWS/NVDA). A full-document single article with heading structure lets them jump to "Section 3" of a contract without pagination friction. Per-page tabs or pagination destroys this. 2. **Browser Find in Page**: Works across the entire visible document. If content is paged, Cmd+F only searches the visible page. A user searching for "indemnification" would not find it on page 4 if they are viewing page 1. 3. **Browser translate**: Chrome's Page Translate works on the visible DOM. A paged text view may not translate the full document in one pass. 4. **Copy/paste**: Users who want to copy a clause from a multi-page document do not want to page through it; they want to Cmd+A or select-and-copy across a continuous document. 5. **Cognitive load**: Pagination introduces navigation overhead. Users with cognitive disabilities benefit from fewer controls, not more. **Single scroll with per-page section headings** gives the best of both worlds: the document reads as a continuous flow (good for linear reading), but AT users can jump to "Page 3" section marker (good for navigation) and sighted users can scroll normally. ### Pitfalls the team might miss #### 1. The tab switcher must remember state across Turbo navigation DocuSeal uses Turbo Drive. If the user switches to Text View and then Turbo navigates away and back, the tab state resets to PDF View. This is probably acceptable behavior (default to PDF on fresh page load), but it should be deliberate. Store the preference in `localStorage` and restore it on `turbo:load`. Do not use a cookie (requires server round-trip, affects legal audit trail unnecessarily). #### 2. The hidden panel must use `hidden` attribute or `display: none`, not `visibility: hidden` `visibility: hidden` keeps the element in the accessibility tree. AT users would navigate into a "hidden" panel. Use `hidden` HTML attribute (maps to `display: none`) or `aria-hidden="true"` on inactive panels. The ARIA tab pattern requires that inactive `tabpanel` elements use `hidden` attribute (not just visual hiding with CSS classes). DaisyUI's tab component may not do this correctly out of the box — verify before shipping. #### 3. Focus management on tab switch When the user clicks/keyboards to a new tab, focus should remain on the newly activated tab button — NOT move into the panel content. The panel becomes accessible by pressing Tab from the active tab. Do not auto-focus the panel on activation. Exception: if the panel was activated via keyboard and the panel has no focusable children, Tab after activating the tab should move into the panel. This is standard APG behavior. #### 4. Text quality disclosure Somewhere in the Text View — either a brief banner or a tooltip on the tab button — inform users that "Text View provides an accessible alternative. The PDF View is the authoritative document." This is not a legal disclaimer (that is overkill) but a brief user-facing note that sets correct expectations. Example: `
This text representation is provided for accessibility. The PDF view is the signed document.
` at the top of the text panel. #### 5. The 15-page extraction cap `MAX_NUMBER_OF_PAGES_PROCESSED = 15` means documents with more than 15 pages will have no text for pages 16+. The Text View must handle this gracefully. Options: - Show text for pages 1-15, then an "i" info message: "Text not available for pages 16 and beyond" - Do not show the Text View tab at all if the document exceeds 15 pages (simpler, avoids partial text confusion) The second option is more conservative and avoids the misleading "there is text but only for some pages" situation. Recommendation: hide the tab if `pages_text` keys count < `number_of_pages`. This is a simple Ruby check. #### 6. RTL document handling The `dir="auto"` attribute on paragraph elements is essential for documents that mix Hebrew, Arabic, or Persian text (which DocuSeal's multilingual user base may encounter). `` lets the browser infer text direction per-paragraph. Without it, RTL text in an LTR container renders as reversed word-soup. #### 7. Do not use `role="tablist"` + DaisyUI checkbox tabs DaisyUI's tab pattern uses `` and CSS `:checked` pseudo-selectors, not the ARIA tab pattern. This is a completely different interaction model and does NOT satisfy APG keyboard behavior. If using DaisyUI, you must either: a. Override DaisyUI tabs with a JavaScript-driven ARIA tab implementation, or b. Use the `