Repository: MudBlazor/MudBlazor (2026-04-04T21:19:35Z)

Issue:
Remove misleading or redundant ARIA/role usage
## Summary

Some components currently add ARIA/role attributes that are either redundant for native controls or actively misleading.

This issue is about removing noise, not adding new semantics.

## Examples

Consider removing:
- `role="slider"` and manual `aria-valuenow` / `aria-valuemin` / `aria-valuemax` from native `input[type=range]`
- `role="radio"` and `aria-disabled` from native radio inputs
- invalid plain `required` from the `radiogroup` wrapper while keeping `aria-required`
- incorrect `role="menu"` from the `MudTimePicker` clock face

## Reasoning

Native HTML controls already provide many semantics automatically. Duplicating or overriding them creates unnecessary noise and can confuse assistive technology.

Incorrect roles are worse than missing roles because they announce the wrong interaction model.

## Acceptance Criteria

- native controls rely on native semantics where appropriate
- invalid or misleading roles are removed
- resulting markup is simpler and more accurate



Discussion:

Patch:
diff --git a/src/MudBlazor/Components/Radio/MudRadio.razor b/src/MudBlazor/Components/Radio/MudRadio.razor
index f40b687cba58..7b461b1e6301 100644
--- a/src/MudBlazor/Components/Radio/MudRadio.razor
+++ b/src/MudBlazor/Components/Radio/MudRadio.razor
@@ -6,7 +6,7 @@
     <InputContent>
         <label class="@LabelClassname" style="@Style" id="@_elementId" @onkeydown="@HandleKeyDownAsync" @onclick:stopPropagation="@StopClickPropagation">
             <span class="@IconClassname">
-                <input aria-labelledby="@(string.IsNullOrWhiteSpace(AriaLabel) ? null : _ariaId)" @attributes="GetInputAttributes()" type="radio" class="mud-radio-input" aria-disabled="@(GetDisabledState().ToString().ToLower())" checked="@Checked" role="radio" name="@MudRadioGroup?.Name" disabled="@GetDisabledState()" @onclick:preventDefault="@GetReadOnlyState()" @onclick="OnClickAsync" />
+                <input aria-labelledby="@(string.IsNullOrWhiteSpace(AriaLabel) ? null : _ariaId)" @attributes="GetInputAttributes()" type="radio" class="mud-radio-input" checked="@Checked" name="@MudRadioGroup?.Name" disabled="@GetDisabledState()" @onclick:preventDefault="@GetReadOnlyState()" @onclick="OnClickAsync" />
                 <MudIcon Disabled="@Disabled" Icon="@GetIcon()" Color="HasErrors ? Color.Error : Color.Inherit" Size="@Size" />
             </span>
             @if (!string.IsNullOrEmpty(Label))
diff --git a/src/MudBlazor/Components/Radio/MudRadioGroup.razor b/src/MudBlazor/Components/Radio/MudRadioGroup.razor
index 86093c519912..2233d7f37e34 100644
--- a/src/MudBlazor/Components/Radio/MudRadioGroup.razor
+++ b/src/MudBlazor/Components/Radio/MudRadioGroup.razor
@@ -14,7 +14,7 @@
             <CascadingValue TValue="IForm" Value="null">
                 <CascadingValue TValue="bool" Name="ParentDisabled" Value="@GetDisabledState()">
                     <CascadingValue TValue="bool" Name="ParentReadOnly" Value="@GetReadOnlyState()">
-                        <div role="radiogroup" @attributes="UserAttributes" class="@GetInputClass()" style="@InputStyle" required="@Required" aria-required="@Required.ToString().ToLowerInvariant()">
+                        <div role="radiogroup" @attributes="UserAttributes" class="@GetInputClass()" style="@InputStyle" aria-required="@Required.ToString().ToLowerInvariant()">
                             @ChildContent
                         </div>
                     </CascadingValue>
diff --git a/src/MudBlazor/Components/Slider/MudSlider.razor b/src/MudBlazor/Components/Slider/MudSlider.razor
index bbc639ef616e..2f51a4c4653c 100644
--- a/src/MudBlazor/Components/Slider/MudSlider.razor
+++ b/src/MudBlazor/Components/Slider/MudSlider.razor
@@ -35,7 +35,7 @@
 			</div>
 		}
         
-        <input type="range" class="mud-slider-input" min="@Min.ToString(null, CultureInfo.InvariantCulture)" max="@Max.ToString(null, CultureInfo.InvariantCulture)" step="@Step.ToString(null, CultureInfo.InvariantCulture)" aria-valuenow="@_valueState.Value" aria-valuemin="@Min.ToString(null, CultureInfo.InvariantCulture)" aria-valuemax="@Max.ToString(null, CultureInfo.InvariantCulture)" role="slider" @attributes="UserAttributes" disabled="@Disabled"
+        <input type="range" class="mud-slider-input" min="@Min.ToString(null, CultureInfo.InvariantCulture)" max="@Max.ToString(null, CultureInfo.InvariantCulture)" step="@Step.ToString(null, CultureInfo.InvariantCulture)" @attributes="UserAttributes" disabled="@Disabled"
                @bind-value:get="@GetValueText" @bind-value:set="@SetValueTextAsync" @bind-value:event="@(Immediate ? "oninput" : "onchange")" />
         @if (ValueLabel)
         {
diff --git a/src/MudBlazor/Components/TimePicker/MudTimePicker.razor b/src/MudBlazor/Components/TimePicker/MudTimePicker.razor
index b6f90d9ad043..c8cc60207878 100644
--- a/src/MudBlazor/Components/TimePicker/MudTimePicker.razor
+++ b/src/MudBlazor/Components/TimePicker/MudTimePicker.razor
@@ -31,7 +31,7 @@
         <MudPickerContent>
             <div class="mud-picker-time-container">
                 <div class="mud-picker-time-clock">
-                    <div role="menu" tabindex="-1" class="mud-picker-time-clock-mask" @ref="ClockElementReference" @oncontextmenu:preventDefault>
+                    <div tabindex="-1" class="mud-picker-time-clock-mask" @ref="ClockElementReference" @oncontextmenu:preventDefault>
                         <div class="@GetClockPinColor()"></div>
                         <div class="@GetClockPointerColor()" style="height: @GetPointerHeight(); transform: @GetPointerRotation()">
                             <div class="@GetClockPointerThumbColor()"></div>


Test patch:
diff --git a/src/MudBlazor.UnitTests.Viewer/TestComponents/RadioGroup/RadioGroupRequiredTest.razor b/src/MudBlazor.UnitTests.Viewer/TestComponents/RadioGroup/RadioGroupRequiredTest.razor
index 19265b355e22..51b1ca3ebd37 100644
--- a/src/MudBlazor.UnitTests.Viewer/TestComponents/RadioGroup/RadioGroupRequiredTest.razor
+++ b/src/MudBlazor.UnitTests.Viewer/TestComponents/RadioGroup/RadioGroupRequiredTest.razor
@@ -9,7 +9,7 @@
 </MudRadioGroup>
 
 @code {
-    public static string __description__ = "Required RadioGroup should have required & aria-required attributes.";
+    public static string __description__ = "Required RadioGroup should have aria-required attributes.";
 
     [Parameter]
     public bool Required { get; set; }
diff --git a/src/MudBlazor.UnitTests/Components/RadioTests.cs b/src/MudBlazor.UnitTests/Components/RadioTests.cs
index d2284c2ad92b..1d5616dff10e 100644
--- a/src/MudBlazor.UnitTests/Components/RadioTests.cs
+++ b/src/MudBlazor.UnitTests/Components/RadioTests.cs
@@ -361,46 +361,33 @@ public async Task RadioGroupReadOnly()
             radioGroup.FindAll(".mud-radio > span.mud-readonly").Count.Should().Be(4);
         }
 
-        /// <summary>
-        /// Optional RadioGroup should not have required attribute and aria-required should be false.
-        /// </summary>
         [Test]
-        public void OptionalRadioGroup_Should_NotHaveRequiredAttributeAndAriaRequiredShouldBeFalse()
+        public void OptionalRadioGroup_Should_HaveAriaRequiredFalse()
         {
             var comp = Context.Render<RadioGroupRequiredTest>();
 
-            comp.Find("div[role=\"radiogroup\"]").HasAttribute("required").Should().BeFalse();
             comp.Find("div[role=\"radiogroup\"]").GetAttribute("aria-required").Should().Be("false");
         }
 
-        /// <summary>
-        /// Required RadioGroup should have required and aria-required attributes.
-        /// </summary>
         [Test]
-        public void RequiredRadioGroup_Should_HaveRequiredAndAriaRequiredAttributes()
+        public void RequiredRadioGroup_Should_HaveAriaRequiredTrue()
         {
             var comp = Context.Render<RadioGroupRequiredTest>(parameters => parameters
                 .Add(p => p.Required, true));
 
-            comp.Find("div[role=\"radiogroup\"]").HasAttribute("required").Should().BeTrue();
             comp.Find("div[role=\"radiogroup\"]").GetAttribute("aria-required").Should().Be("true");
         }
 
-        /// <summary>
-        /// Required and aria-required RadioGroup attributes should be dynamic.
-        /// </summary>
         [Test]
-        public async Task RequiredAndAriaRequiredRadioGroupAttributes_Should_BeDynamic()
+        public async Task RadioGroupAriaRequired_Should_BeDynamic()
         {
             var comp = Context.Render<RadioGroupRequiredTest>();
 
-            comp.Find("div[role=\"radiogroup\"]").HasAttribute("required").Should().BeFalse();
             comp.Find("div[role=\"radiogroup\"]").GetAttribute("aria-required").Should().Be("false");
 
             await comp.SetParametersAndRenderAsync(parameters => parameters
                 .Add(p => p.Required, true));
 
-            comp.Find("div[role=\"radiogroup\"]").HasAttribute("required").Should().BeTrue();
             comp.Find("div[role=\"radiogroup\"]").GetAttribute("aria-required").Should().Be("true");
         }
 
diff --git a/src/MudBlazor.UnitTests/Components/SliderTests.cs b/src/MudBlazor.UnitTests/Components/SliderTests.cs
index 5d013a3afd94..45f890868496 100644
--- a/src/MudBlazor.UnitTests/Components/SliderTests.cs
+++ b/src/MudBlazor.UnitTests/Components/SliderTests.cs
@@ -130,10 +130,6 @@ public void GeneralStructure()
 
             var expectedAttributes = new Dictionary<string, string>()
             {
-                { "aria-valuenow","120" },
-                { "aria-valuemin","100" },
-                { "aria-valuemax","200" },
-                { "role","slider" },
                 { "min","100" },
                 { "max","200" },
                 { "step","10" },