diff --git a/docs/docs/components/radio.md b/docs/docs/components/radio.md
index 828c6c059..8453f1e7c 100644
--- a/docs/docs/components/radio.md
+++ b/docs/docs/components/radio.md
@@ -74,3 +74,15 @@ Add the `size` attribute to the [Radio Group](/docs/components/radio-group) to c
Large 3
```
+
+### Hint
+
+Add descriptive hint to a switch with the `hint` attribute. For hints that contain HTML, use the `hint` slot instead.
+
+```html {.example}
+
+ Option 1
+ Option 2
+ Option 3
+
+```
diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts
index 779f6be77..d60ba8c29 100644
--- a/src/components/checkbox/checkbox.ts
+++ b/src/components/checkbox/checkbox.ts
@@ -127,7 +127,7 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@property({ type: Boolean, reflect: true }) required = false;
/** The checkbox's hint. If you need to display HTML, use the `hint` slot instead. */
- @property({ attribute: 'hint' }) hint = '';
+ @property() hint = '';
private handleClick() {
this.hasInteracted = true;
diff --git a/src/components/radio/radio.css b/src/components/radio/radio.css
index 984e54f7b..df4281da8 100644
--- a/src/components/radio/radio.css
+++ b/src/components/radio/radio.css
@@ -1,15 +1,15 @@
-:host(:focus-visible) {
- outline: none;
-}
-
-.radio {
- display: flex;
+:host {
+ display: grid;
+ grid-template-columns: auto 1fr;
align-items: top;
- font: inherit;
vertical-align: middle;
cursor: pointer;
}
+:host(:focus-visible) {
+ outline: none;
+}
+
.checked-icon {
display: flex;
fill: currentColor;
@@ -19,6 +19,11 @@
}
/* When the control isn't checked, hide the circle for Windows High Contrast mode a11y */
-.radio:not(.radio--checked) svg circle {
+:host(:not(:state(checked))) svg circle {
opacity: 0;
}
+
+[part~='hint'] {
+ grid-column: 2;
+ margin-block-start: var(--wa-space-3xs);
+}
diff --git a/src/components/radio/radio.ts b/src/components/radio/radio.ts
index 11946b8ea..435a3a6cb 100644
--- a/src/components/radio/radio.ts
+++ b/src/components/radio/radio.ts
@@ -1,9 +1,11 @@
+import type { PropertyValues } from 'lit';
import { html, isServer } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
-import { watch } from '../../internal/watch.js';
+import { HasSlotController } from '../../internal/slot.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import nativeStyles from '../../styles/native/radio.css';
+import formControlStyles from '../../styles/shadow/form-control.css';
import sizeStyles from '../../styles/utilities/size.css';
import '../icon/icon.js';
import styles from './radio.css';
@@ -17,14 +19,15 @@ import styles from './radio.css';
* @dependency wa-icon
*
* @slot - The radio's label.
+ * @slot hint - Text that describes how to use the checkbox. Alternatively, you can use the `hint` attribute.
*
* @event blur - Emitted when the control loses focus.
* @event focus - Emitted when the control gains focus.
*
- * @csspart base - The component's base wrapper.
* @csspart control - The circular container that wraps the radio's checked state.
* @csspart checked-icon - The checked icon.
* @csspart label - The container that wraps the radio's label.
+ * @csspart hint - The hint's wrapper.
*
* @cssproperty --background-color - The radio's background color.
* @cssproperty --background-color-checked - The radio's background color when checked.
@@ -42,7 +45,7 @@ import styles from './radio.css';
*/
@customElement('wa-radio')
export default class WaRadio extends WebAwesomeFormAssociatedElement {
- static shadowStyle = [sizeStyles, nativeStyles, styles];
+ static shadowStyle = [formControlStyles, sizeStyles, nativeStyles, styles];
@state() checked = false;
@@ -63,6 +66,11 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
/** Disables the radio. */
@property({ type: Boolean }) disabled = false;
+ /** The radio's hint. If you need to display HTML, use the `hint` slot instead. */
+ @property() hint = '';
+
+ private readonly hasSlotController = new HasSlotController(this, 'hint');
+
constructor() {
super();
if (!isServer) {
@@ -81,11 +89,19 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
- @watch('checked')
- handleCheckedChange() {
- this.toggleCustomState('checked', this.checked);
- this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
- this.tabIndex = this.checked ? 0 : -1;
+ updated(changedProperties: PropertyValues) {
+ super.updated(changedProperties);
+
+ if (changedProperties.has('checked')) {
+ this.toggleCustomState('checked', this.checked);
+ this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
+ this.tabIndex = this.checked ? 0 : -1;
+ }
+
+ if (changedProperties.has('disabled')) {
+ this.toggleCustomState('disabled', this.disabled);
+ this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
+ }
}
/**
@@ -95,12 +111,6 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
// We override `setValue` because we don't want to set form values from here. We want to do that in "RadioGroup" itself.
}
- @watch('disabled', { waitUntilFirstUpdate: true })
- handleDisabledChange() {
- this.toggleCustomState('disabled', this.disabled);
- this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
- }
-
private handleClick = () => {
if (!this.disabled) {
this.checked = true;
@@ -108,26 +118,30 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
};
render() {
- return html`
-
-
- ${this.checked
- ? html`
-
- `
- : ''}
-
+ const hasHintSlot = isServer ? true : this.hasSlotController.test('hint');
+ const hasHint = this.hint ? true : !!hasHintSlot;
-
+ return html`
+
+ ${this.checked
+ ? html`
+
+ `
+ : ''}
+
+
+
+ ${this.hint}
`;
}
}
diff --git a/src/styles/native/radio.css b/src/styles/native/radio.css
index 46f005a8a..dbd67fb55 100644
--- a/src/styles/native/radio.css
+++ b/src/styles/native/radio.css
@@ -71,13 +71,13 @@ input[type='radio'],
input[type='radio'],
label:has(input[type='radio']),
input[type='radio'] + label,
-:host [part~='base'] {
+:host {
cursor: pointer;
}
/* Checked */
input[type='radio']:checked,
-:host .radio--checked .control {
+:host(:state(checked)) .control {
color: var(--checked-icon-color);
border-color: var(--border-color-checked);
background-color: var(--background-color-checked);