Skip to content

feat(textarea): add <rh-textarea>#3006

Open
adamjohnson wants to merge 37 commits into
mainfrom
feat/rh-textarea
Open

feat(textarea): add <rh-textarea>#3006
adamjohnson wants to merge 37 commits into
mainfrom
feat/rh-textarea

Conversation

@adamjohnson

@adamjohnson adamjohnson commented May 5, 2026

Copy link
Copy Markdown
Collaborator

What I did

  1. Added <rh-textarea> element.
  2. Closes [feat] Create <rh-textarea> component/pattern #2323.

Testing Instructions

  1. View the overview page on the DP.
  2. View the following demos:
  3. Ensure the textarea visually matches the Form pattern specs for both default and horizontal label orientations.
  4. Ensure the correct design tokens are used in the CSS.
  5. Submit the required demo with an empty textarea — verify the browser validation message appears.
  6. Test disabled textarea is focusable via keyboard (uses aria-disabled, not native disabled).
  7. Test in Chrome, Firefox, and Safari.
  8. Switch between light and dark color schemes and verify colors adapt.
  9. Test the field-sizing="content" demo — textarea should auto-grow in Chrome/Safari but fall back to fixed height in Firefox.
  10. Review Code and Accessibility docs for accuracy.

Notes to Reviewers

Demos on the DP are currently broken due to a docs server rendering issue. You will have to clone this locally to properly view/test it. DP Demos now work!

Does this element need a dedicated design spec? Or are the Form pattern specs sufficient?

The element uses aria-disabled instead of native disabled so the textarea remains focusable for assistive technology users. This matches the pattern used by rh-button, rh-switch, and other RHDS form controls.

field-sizing="content" is a progressive enhancement — it uses the CSS field-sizing property which is not supported in Firefox. Firefox users get a standard fixed-height textarea.

To do's

  • Component
  • Demos
  • Tests
  • Docs
  • Changeset
  • screenshot.png
  • Remove temporary Netlify redirect from netlify.toml
  • SSR support (eleventy.config.ts — requires fixing docs server rendering first)

adamjohnson and others added 5 commits April 29, 2026 09:18
* feat(scheme-toggle): add the `scheme-changed` event

* test(scheme-toggle): add tests for `scheme-changed` event

* chore(scheme-toggle): add changeset

* fix(scheme-toggle): harmonize `isServer` code style

* test(scheme-toggle): listen before fixture in init test

* docs(scheme-toggle): improve event CEM health docs

* docs(scheme-toggle): update demo description formats to match #2951

* docs(scheme-toggle): wrap events demo in a form

* test(scheme-toggle): use `@scheme-changed` template binding for event tests

* test(scheme-toggle): refactor tests to use ax tree helpers and avoid querying shadowdom
@adamjohnson adamjohnson self-assigned this May 5, 2026
@adamjohnson adamjohnson added the feature New feature or request label May 5, 2026
@netlify

netlify Bot commented May 5, 2026

Copy link
Copy Markdown

Deploy Preview for red-hat-design-system ready!

Name Link
🔨 Latest commit 46ce75d
🔍 Latest deploy log https://app.netlify.com/projects/red-hat-design-system/deploys/6a19fc4977b40f0008c67e55
😎 Deploy Preview https://deploy-preview-3006--red-hat-design-system.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented May 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 46ce75d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@adamjohnson adamjohnson moved this to Review 🔍 in Red Hat Design System May 5, 2026
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Size Change: +5.73 kB (+1.82%)

Total Size: 320 kB

📦 View Changed
Filename Size Change
elements.js 842 B +8 B (+0.96%)
elements/rh-textarea/rh-textarea.js 5.55 kB +5.55 kB (new file) 🆕
react/rh-textarea/rh-textarea.js 176 B +176 B (new file) 🆕
ℹ️ View Unchanged
Filename Size
elements/rh-accordion/context.js 162 B
elements/rh-accordion/rh-accordion-header.js 3.33 kB
elements/rh-accordion/rh-accordion-panel.js 2.08 kB
elements/rh-accordion/rh-accordion.js 3.67 kB
elements/rh-alert/rh-alert.js 5.52 kB
elements/rh-announcement/rh-announcement.js 2.84 kB
elements/rh-audio-player/rh-audio-player-about.js 2.28 kB
elements/rh-audio-player/rh-audio-player-rate-stepper.js 2.07 kB
elements/rh-audio-player/rh-audio-player-scrolling-text-overflow.js 1.78 kB
elements/rh-audio-player/rh-audio-player-subscribe.js 1.9 kB
elements/rh-audio-player/rh-audio-player.js 13.2 kB
elements/rh-audio-player/rh-cue.js 2.32 kB
elements/rh-audio-player/rh-transcript.js 3.38 kB
elements/rh-avatar/random-pattern-controller.js 2.72 kB
elements/rh-avatar/rh-avatar.js 3.45 kB
elements/rh-back-to-top/rh-back-to-top.js 2.79 kB
elements/rh-badge/rh-badge.js 2.05 kB
elements/rh-blockquote/rh-blockquote.js 1.93 kB
elements/rh-breadcrumb/rh-breadcrumb.js 2.34 kB
elements/rh-button-group/rh-button-group.js 937 B
elements/rh-button/rh-button.js 4.1 kB
elements/rh-card/rh-card.js 3.46 kB
elements/rh-chip/context.js 165 B
elements/rh-chip/rh-chip-group.js 1.98 kB
elements/rh-chip/rh-chip.js 2.45 kB
elements/rh-code-block/prism.css.js 667 B
elements/rh-code-block/prism.js 572 B
elements/rh-code-block/rh-code-block.js 9.21 kB
elements/rh-cta/rh-cta.js 3.9 kB
elements/rh-dialog/rh-dialog.js 5.04 kB
elements/rh-dialog/yt-api.js 617 B
elements/rh-disclosure/rh-disclosure.js 3.41 kB
elements/rh-footer/rh-footer-block.js 1.07 kB
elements/rh-footer/rh-footer-copyright.js 649 B
elements/rh-footer/rh-footer-links.js 1.65 kB
elements/rh-footer/rh-footer-social-link.js 1.24 kB
elements/rh-footer/rh-footer-universal.js 5.37 kB
elements/rh-footer/rh-footer.js 5.85 kB
elements/rh-health-index/rh-health-index.js 2.48 kB
elements/rh-icon/rh-icon.js 2.98 kB
elements/rh-icon/ssr.js 181 B
elements/rh-jump-links/context.js 179 B
elements/rh-jump-links/rh-jump-link.js 1.89 kB
elements/rh-jump-links/rh-jump-links-list.js 1.67 kB
elements/rh-jump-links/rh-jump-links.js 3.08 kB
elements/rh-menu-dropdown/rh-menu-dropdown.js 4.43 kB
elements/rh-menu/rh-menu-item-group.js 941 B
elements/rh-menu/rh-menu-item.js 2.55 kB
elements/rh-menu/rh-menu.js 2.04 kB
elements/rh-navigation-link/rh-navigation-link.js 2.13 kB
elements/rh-navigation-primary/context.js 176 B
elements/rh-navigation-primary/rh-navigation-primary-item-menu.js 1.41 kB
elements/rh-navigation-primary/rh-navigation-primary-item.js 4.23 kB
elements/rh-navigation-primary/rh-navigation-primary.js 9.95 kB
elements/rh-navigation-secondary/rh-navigation-secondary-dropdown.js 3.15 kB
elements/rh-navigation-secondary/rh-navigation-secondary-menu-section.js 2.02 kB
elements/rh-navigation-secondary/rh-navigation-secondary-menu.js 2.01 kB
elements/rh-navigation-secondary/rh-navigation-secondary-overlay.js 828 B
elements/rh-navigation-secondary/rh-navigation-secondary.js 6.32 kB
elements/rh-navigation-secondary/test/fixtures.js 769 B
elements/rh-navigation-vertical/rh-navigation-vertical-list.js 2.56 kB
elements/rh-navigation-vertical/rh-navigation-vertical.js 1.65 kB
elements/rh-pagination/rh-pagination.js 6.51 kB
elements/rh-progress-stepper/context.js 187 B
elements/rh-progress-stepper/rh-progress-step.js 3.02 kB
elements/rh-progress-stepper/rh-progress-stepper.js 4.78 kB
elements/rh-readtime/rh-readtime.js 2.93 kB
elements/rh-scheme-toggle/rh-scheme-toggle.js 2.81 kB
elements/rh-select/rh-option-group.js 1.6 kB
elements/rh-select/rh-option.js 2.39 kB
elements/rh-select/rh-select.js 9.46 kB
elements/rh-site-status/rh-site-status.js 2.53 kB
elements/rh-skeleton/rh-skeleton.js 742 B
elements/rh-skip-link/rh-skip-link.js 1.45 kB
elements/rh-spinner/rh-spinner.js 1.51 kB
elements/rh-stat/rh-stat.js 2.71 kB
elements/rh-subnav/rh-subnav.js 3.18 kB
elements/rh-surface/rh-surface.js 865 B
elements/rh-surface/test/elements.js 763 B
elements/rh-switch/rh-switch.js 3.22 kB
elements/rh-table/rh-sort-button.js 1.88 kB
elements/rh-table/rh-table.js 3.27 kB
elements/rh-tabs/context.js 223 B
elements/rh-tabs/rh-tab-panel.js 1.19 kB
elements/rh-tabs/rh-tab.js 3.64 kB
elements/rh-tabs/rh-tabs.js 4.35 kB
elements/rh-tag/rh-tag.js 3.32 kB
elements/rh-tile/rh-tile-group.js 1.97 kB
elements/rh-tile/rh-tile.js 5.09 kB
elements/rh-timestamp/rh-timestamp.js 1.33 kB
elements/rh-tooltip/rh-tooltip.js 3.4 kB
elements/rh-video-embed/rh-video-embed.js 5.45 kB
lib/color-palettes.js 851 B
lib/context/headings/consumer.js 591 B
lib/context/headings/provider.js 1.2 kB
lib/elements/rh-context-demo/rh-context-demo.js 1.16 kB
lib/elements/rh-context-picker/rh-context-picker.js 2.18 kB
lib/environment.js 194 B
lib/functions.js 175 B
lib/I18nController.js 1.37 kB
lib/ScreenSizeController.js 876 B
lib/ssr-controller.js 201 B
lib/themable.js 549 B
react/lib/color-palettes.js 97 B
react/lib/context/headings/consumer.js 103 B
react/lib/context/headings/provider.js 105 B
react/lib/elements/rh-context-demo/rh-context-demo.js 186 B
react/lib/elements/rh-context-picker/rh-context-picker.js 189 B
react/lib/functions.js 92 B
react/lib/I18nController.js 97 B
react/lib/ScreenSizeController.js 102 B
react/lib/ssr-controller.js 97 B
react/lib/themable.js 91 B
react/rh-accordion/rh-accordion-header.js 199 B
react/rh-accordion/rh-accordion-panel.js 185 B
react/rh-accordion/rh-accordion.js 202 B
react/rh-alert/rh-alert.js 184 B
react/rh-announcement/rh-announcement.js 189 B
react/rh-audio-player/rh-audio-player-about.js 191 B
react/rh-audio-player/rh-audio-player-rate-stepper.js 223 B
react/rh-audio-player/rh-audio-player-scrolling-text-overflow.js 214 B
react/rh-audio-player/rh-audio-player-subscribe.js 196 B
react/rh-audio-player/rh-audio-player.js 183 B
react/rh-audio-player/rh-cue.js 195 B
react/rh-audio-player/rh-transcript.js 207 B
react/rh-avatar/rh-avatar.js 173 B
react/rh-back-to-top/rh-back-to-top.js 183 B
react/rh-badge/rh-badge.js 174 B
react/rh-blockquote/rh-blockquote.js 179 B
react/rh-breadcrumb/rh-breadcrumb.js 179 B
react/rh-button-group/rh-button-group.js 184 B
react/rh-button/rh-button.js 174 B
react/rh-card/rh-card.js 172 B
react/rh-chip/rh-chip-group.js 182 B
react/rh-chip/rh-chip.js 180 B
react/rh-code-block/rh-code-block.js 193 B
react/rh-cta/rh-cta.js 170 B
react/rh-dialog/rh-dialog.js 203 B
react/rh-disclosure/rh-disclosure.js 192 B
react/rh-footer/rh-footer-block.js 184 B
react/rh-footer/rh-footer-copyright.js 187 B
react/rh-footer/rh-footer-links.js 185 B
react/rh-footer/rh-footer-social-link.js 193 B
react/rh-footer/rh-footer-universal.js 188 B
react/rh-footer/rh-footer.js 174 B
react/rh-health-index/rh-health-index.js 184 B
react/rh-icon/rh-icon.js 195 B
react/rh-jump-links/rh-jump-link.js 183 B
react/rh-jump-links/rh-jump-links-list.js 189 B
react/rh-jump-links/rh-jump-links.js 195 B
react/rh-menu-dropdown/rh-menu-dropdown.js 198 B
react/rh-menu/rh-menu-item-group.js 190 B
react/rh-menu/rh-menu-item.js 181 B
react/rh-menu/rh-menu.js 182 B
react/rh-navigation-link/rh-navigation-link.js 186 B
react/rh-navigation-primary/rh-navigation-primary-item-menu.js 205 B
react/rh-navigation-primary/rh-navigation-primary-item.js 210 B
react/rh-navigation-primary/rh-navigation-primary.js 189 B
react/rh-navigation-secondary/rh-navigation-secondary-dropdown.js 227 B
react/rh-navigation-secondary/rh-navigation-secondary-menu-section.js 205 B
react/rh-navigation-secondary/rh-navigation-secondary-menu.js 199 B
react/rh-navigation-secondary/rh-navigation-secondary-overlay.js 201 B
react/rh-navigation-secondary/rh-navigation-secondary.js 213 B
react/rh-navigation-vertical/rh-navigation-vertical-list.js 209 B
react/rh-navigation-vertical/rh-navigation-vertical.js 189 B
react/rh-pagination/rh-pagination.js 178 B
react/rh-progress-stepper/rh-progress-step.js 196 B
react/rh-progress-stepper/rh-progress-stepper.js 186 B
react/rh-readtime/rh-readtime.js 175 B
react/rh-scheme-toggle/rh-scheme-toggle.js 200 B
react/rh-select/rh-option-group.js 187 B
react/rh-select/rh-option.js 177 B
react/rh-select/rh-select.js 204 B
react/rh-site-status/rh-site-status.js 181 B
react/rh-skeleton/rh-skeleton.js 176 B
react/rh-skip-link/rh-skip-link.js 181 B
react/rh-spinner/rh-spinner.js 175 B
react/rh-stat/rh-stat.js 171 B
react/rh-subnav/rh-subnav.js 175 B
react/rh-surface/rh-surface.js 175 B
react/rh-switch/rh-switch.js 185 B
react/rh-table/rh-sort-button.js 200 B
react/rh-table/rh-table.js 174 B
react/rh-tabs/rh-tab-panel.js 181 B
react/rh-tabs/rh-tab.js 187 B
react/rh-tabs/rh-tabs.js 184 B
react/rh-tag/rh-tag.js 171 B
react/rh-tile/rh-tile-group.js 183 B
react/rh-tile/rh-tile.js 181 B
react/rh-timestamp/rh-timestamp.js 176 B
react/rh-tooltip/rh-tooltip.js 175 B
react/rh-video-embed/rh-video-embed.js 227 B
uxdot/ssr-adopt-directive.js 2.02 kB
uxdot/uxdot-best-practice.js 812 B
uxdot/uxdot-copy-button.js 1.24 kB
uxdot/uxdot-copy-permalink.js 1.14 kB
uxdot/uxdot-demo.js 2.89 kB
uxdot/uxdot-example.js 1.14 kB
uxdot/uxdot-feedback.js 983 B
uxdot/uxdot-header.js 886 B
uxdot/uxdot-knob-attribute.js 3.73 kB
uxdot/uxdot-masthead.js 1.45 kB
uxdot/uxdot-pattern-ssr-controller-client.js 386 B
uxdot/uxdot-pattern-ssr-controller-server.js 1.71 kB
uxdot/uxdot-pattern-ssr-controller.js 213 B
uxdot/uxdot-pattern.js 2.37 kB
uxdot/uxdot-repo-status-checklist.js 1.39 kB
uxdot/uxdot-repo-status-list.js 1.24 kB
uxdot/uxdot-repo.js 867 B
uxdot/uxdot-sidenav.js 2.04 kB
uxdot/uxdot-spacer-tokens-table.js 2.45 kB
uxdot/uxdot-toc.js 1.89 kB

compressed-size-action

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Documentation Health

Module Score
rh-textarea/rh-textarea.js 83/100 83%
Overall 83/100 83%
rh-textarea (RhTextarea) — 83/100 ✅
Category Score
Element description 18/25 ⚠️
Attribute documentation 20/20
Slot documentation 10/15 ⚠️
CSS documentation 10/15 ⚠️
Event documentation 15/15
Demos 10/10

Recommendations:

  1. rh-textarea: use RFC 2119 keywords (MUST, SHOULD, AVOID) to clarify requirements (Element description, +5 pts)
  2. rh-textarea: mention accessibility considerations for slot content (Slot documentation, +5 pts)
  3. rh-textarea: reference design tokens or theme considerations in CSS descriptions (CSS documentation, +4 pts)
  4. rh-textarea: describe purpose and context using words like 'for', 'when', 'provides', 'allows' (Element description, +2 pts)
  5. rh-textarea: add descriptions to all CSS custom properties (CSS documentation, +1 pts)

@adamjohnson adamjohnson linked an issue May 5, 2026 that may be closed by this pull request
2 tasks
@adamjohnson
adamjohnson marked this pull request as ready for review May 5, 2026 20:39
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add &lt;rh-textarea&gt; web component with form validation and accessibility

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Introduces <rh-textarea> web component with form association
• Supports validation (required, minlength, maxlength) and state feedback
• Includes character counter, help text, and auto-grow capabilities
• Provides comprehensive demos and accessibility documentation
Diagram
flowchart LR
  A["rh-textarea Component"] --> B["Form Association"]
  A --> C["Validation API"]
  A --> D["Accessibility Features"]
  B --> E["FormData Integration"]
  B --> F["Form Reset Support"]
  C --> G["required/minlength/maxlength"]
  C --> H["state Feedback"]
  D --> I["ARIA Labeling"]
  D --> J["Help Text Wiring"]
  A --> K["8 Demo Pages"]
  A --> L["Tests & Docs"]
Loading

Grey Divider

File Changes

1. elements/rh-textarea/rh-textarea.ts ✨ Enhancement +484/-0

Core textarea component implementation

elements/rh-textarea/rh-textarea.ts


2. elements/rh-textarea/rh-textarea.css ✨ Enhancement +172/-0

Styling with design tokens and states

elements/rh-textarea/rh-textarea.css


3. elements/rh-textarea/test/rh-textarea.spec.ts 🧪 Tests +465/-0

Comprehensive test suite for component

elements/rh-textarea/test/rh-textarea.spec.ts


View more (14)
4. elements/rh-textarea/demo/index.html 📝 Documentation +26/-0

Basic textarea with label demo

elements/rh-textarea/demo/index.html


5. elements/rh-textarea/demo/required.html 📝 Documentation +38/-0

Required field validation demo

elements/rh-textarea/demo/required.html


6. elements/rh-textarea/demo/states.html 📝 Documentation +52/-0

Success/warning/danger state demos

elements/rh-textarea/demo/states.html


7. elements/rh-textarea/demo/disabled.html 📝 Documentation +35/-0

Disabled textarea with aria-disabled demo

elements/rh-textarea/demo/disabled.html


8. elements/rh-textarea/demo/readonly.html 📝 Documentation +34/-0

Read-only textarea form submission demo

elements/rh-textarea/demo/readonly.html


9. elements/rh-textarea/demo/horizontal.html 📝 Documentation +63/-0

Horizontal label layout demo

elements/rh-textarea/demo/horizontal.html


10. elements/rh-textarea/demo/field-sizing.html 📝 Documentation +34/-0

Auto-growing textarea with field-sizing demo

elements/rh-textarea/demo/field-sizing.html


11. elements/rh-textarea/demo/character-count.html 📝 Documentation +29/-0

Character counter with maxlength demo

elements/rh-textarea/demo/character-count.html


12. elements/rh-textarea/demo/color-context.html 📝 Documentation +28/-0

Light/dark theme color adaptation demo

elements/rh-textarea/demo/color-context.html


13. elements/rh-textarea/README.md 📝 Documentation +22/-0

Installation and basic usage guide

elements/rh-textarea/README.md


14. elements/rh-textarea/docs/00-overview.md 📝 Documentation +5/-0

Overview of textarea use cases

elements/rh-textarea/docs/00-overview.md


15. elements/rh-textarea/docs/30-code.md 📝 Documentation +60/-0

Validation and error state implementation guide

elements/rh-textarea/docs/30-code.md


16. elements/rh-textarea/docs/40-accessibility.md 📝 Documentation +48/-0

Accessibility guidelines and ARIA patterns

elements/rh-textarea/docs/40-accessibility.md


17. elements/rh-textarea/docs/data.yaml ⚙️ Configuration changes +14/-0

Element metadata and schema configuration

elements/rh-textarea/docs/data.yaml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (1)

Grey Divider


Action required

1. figma design marked planned 📎 Requirement gap ⚙ Maintainability
Description
The new ` metadata indicates the Figma/design artifact is still planned`, which does not meet the
requirement that design work be completed and available to the team. This can lead to
implementing/styling the component without an approved, auditable design spec.
Code

elements/rh-textarea/docs/data.yaml[R6-10]

Evidence
PR Compliance ID 2 requires completed design artifacts/specs for ``. The added component metadata
explicitly marks the design source (figma) as planned, indicating the design is not
completed/available.

Design work completed for <rh-textarea>
elements/rh-textarea/docs/data.yaml[6-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`<rh-textarea>` design work appears incomplete because the element metadata lists `libraries.figma: planned`, which conflicts with the compliance requirement that design artifacts/specs are completed and available.
## Issue Context
The compliance checklist (PR Compliance ID 2) requires completed design artifacts/specs (e.g., Figma or equivalent). The current metadata suggests the component is being shipped without completed design.
## Fix Focus Areas
- elements/rh-textarea/docs/data.yaml[6-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. aria-labelledby not supported ✓ Resolved 🐞 Bug ≡ Correctness
Description
rh-textarea only forwards the host’s aria-label to the inner <textarea> and ignores host
aria-labelledby, so consumers using aria-labelledby on <rh-textarea> can end up with an unnamed
textbox in the accessibility tree. This contradicts the component docs that instruct authors to set
ARIA attributes on the host element.
Code

elements/rh-textarea/rh-textarea.ts[R426-438]

Evidence
#syncAccessibleName() checks only the host’s aria-label, then falls back to associated <label>
elements and accessible-label; it never reads host ariaLabelledByElements / aria-labelledby, so that
authoring pattern is not honored. The docs explicitly instruct setting ARIA attributes on the host,
and rh-select demonstrates honoring author-provided aria-labelledby via host.ariaLabelledByElements.

elements/rh-textarea/rh-textarea.ts[420-448]
elements/rh-textarea/docs/30-code.md[58-60]
elements/rh-textarea/docs/40-accessibility.md[23-26]
elements/rh-select/rh-select.ts[491-507]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`<rh-textarea>` forwards the host’s `aria-label` to the inner `<textarea>`, but does not honor `aria-labelledby` set on the host (or `host.ariaLabelledByElements`). This can leave the actual focusable textbox unnamed for assistive tech when consumers label via `aria-labelledby`.
### Issue Context
Docs instruct consumers to set ARIA attributes on the host element. Other RHDS controls (e.g. `rh-select`) explicitly honor author-provided `aria-labelledby` via `ariaLabelledByElements`.
### Fix Focus Areas
- elements/rh-textarea/rh-textarea.ts[420-448]
### What to change
- In `#syncAccessibleName()`, before the `aria-label` branch (or alongside it), check for author-provided labeling via:
- `const authorLabelledBy = (this as HTMLElement).ariaLabelledByElements;`
- and/or `this.getAttribute('aria-labelledby')`
- If present, apply it to the inner textarea via `textarea.ariaLabelledByElements = [...authorLabelledBy]` (or by resolving the ids from `aria-labelledby`), and clear `aria-label` on the textarea.
- Consider ensuring this re-runs when host `aria-label`/`aria-labelledby` changes (e.g. by requesting an update when those attributes change), so dynamic updates don’t leave stale inner labeling.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Disabled validity can go stale ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
disabledChanged() claims it “re-syncs form value and validity” but it only updates form value and
never calls #updateValidity(), so ElementInternals.validity/validationMessage can remain stale after
toggling disabled. This is observable via the component’s validity/validationMessage getters, which
directly expose ElementInternals state.
Code

elements/rh-textarea/rh-textarea.ts[R257-265]

Evidence
The disabled observer comment promises a validity resync, but no validity update occurs. Separately,
#updateValidity() contains a disabled-specific branch that clears validity (setValidity({})) but
that branch won’t run on disabled changes unless #updateValidity() is called, leaving the exposed
validity getter potentially inconsistent with current disabled state.

elements/rh-textarea/rh-textarea.ts[257-265]
elements/rh-textarea/rh-textarea.ts[374-412]
elements/rh-textarea/rh-textarea.ts[356-364]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`disabledChanged()` says it re-syncs validity but does not call `#updateValidity()`, so `ElementInternals.validity` (and `validationMessage`) can be stale after toggling `disabled`.
### Issue Context
The component exposes `validity` and `validationMessage` getters that directly read from `ElementInternals`, so keeping internals validity aligned with current disabled state is important for consumers.
### Fix Focus Areas
- elements/rh-textarea/rh-textarea.ts[257-272]
- elements/rh-textarea/rh-textarea.ts[374-412]
### What to change
- Either:
- Call `this.#updateValidity()` at the end of `disabledChanged()` to ensure internals validity is immediately consistent when disabled toggles.
- Or (minimum):
- Update the doc comment on `disabledChanged()` to remove the claim that validity is resynced, if you intentionally only want validity updates to happen via `checkValidity()/reportValidity()` and value/required changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread elements/rh-textarea/docs/data.yaml
Comment thread elements/rh-textarea/rh-textarea.ts
Comment thread elements/rh-textarea/demo/horizontal.html Outdated
Comment thread elements/rh-textarea/docs/40-accessibility.md
Comment thread elements/rh-textarea/rh-textarea.ts
Comment thread elements/rh-textarea/rh-textarea.ts Outdated
Comment thread elements/rh-textarea/rh-textarea.ts Outdated
Comment thread elements/rh-textarea/rh-textarea.ts Outdated
Comment thread elements/rh-textarea/rh-textarea.ts
@adamjohnson
adamjohnson requested a review from marionnegp May 7, 2026 19:37
Comment thread elements/rh-textarea/rh-textarea.ts

@marionnegp marionnegp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • In light scheme, the read-only version should use surface-lighter as background and gray-20 (no semantic token) for the border. In dark scheme, the read-only's background should be surface-darker and border should be gray-70.
  • Can the success, warning, and danger icons also be positioned inside the text area?
  • Can the margin-block-start for the character count text be 8px? We don't have that specced out in Figma, but then it'll match the spacing for helper text. (The spacing image on the Form pattern page will have to be updated.)

@adamjohnson

Copy link
Copy Markdown
Collaborator Author

Can the success, warning, and danger icons also be positioned inside the text area?

@marionnegp Yes, they can. That said, these icons may appear / disappear on individual keystrokes depending on the form validation rules people implement. This might cause text reflow / layout jank if we don't keep a permanent spot for them (aka increase padding of the textarea).

I want to avoid text reflow. The question then becomes, should we make a permanent spot for them? If so, what would that look like?

Thanks for the review!

@qodo-for-redhat-ux

This comment was marked as outdated.

@bennypowers

Copy link
Copy Markdown
Member

we talked about some trade offs on call today:

  • lift-and-chop aria-attributes: it's not ideal from an api perspective, and may lead to unintentional xroot aria issues, but might serve as a useful backstop if downstream authors do the wrong thing. shipping this backstop however does mean adding js burden to clients, only to service misbehaving downstream devs.
  • accessible-describedby: same xroot aria issue as before, and it would be an innovation. accessible-label is always a scalar, passing a reference is the issue
  • requiring the use of a subset of description elements (e.g. tooltip, popover, helptext) - allows us to control the ax tree, but requires downstream to actually abide by the guidelines

i'm leaning towards the third option, possibly with the backstop as well.

Base automatically changed from staging/growlithe to main July 13, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

Status: Review 🔍

Development

Successfully merging this pull request may close these issues.

[feat] Create <rh-textarea> component/pattern

5 participants