diff --git a/es-ds-components/components/es-form-radio-card.vue b/es-ds-components/components/es-form-radio-card.vue
new file mode 100644
index 000000000..1fac66c65
--- /dev/null
+++ b/es-ds-components/components/es-form-radio-card.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+ {{ displayName }}
+
+
+
diff --git a/es-ds-components/components/es-form-radio-cards.vue b/es-ds-components/components/es-form-radio-cards.vue
new file mode 100644
index 000000000..3e0922fed
--- /dev/null
+++ b/es-ds-components/components/es-form-radio-cards.vue
@@ -0,0 +1,78 @@
+
+
+
+
+
+ {{ props.label }}
+
+
+
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+
diff --git a/es-ds-components/components/es-radio-button-group.vue b/es-ds-components/components/es-radio-button-group.vue
index 80ce3aa85..e7a0ffb1d 100644
--- a/es-ds-components/components/es-radio-button-group.vue
+++ b/es-ds-components/components/es-radio-button-group.vue
@@ -1,14 +1,6 @@
@@ -38,24 +31,23 @@ const model = defineModel();
:id="id"
class="form-group">
- {{ label }}
+ {{ props.label }}
-
-
-
-
+
-
+ :name="props.name"
+ :value="option.value"
+ :model-value="props.modelValue"
+ :inline="props.inline || false"
+ :display-name="option.text"
+ @update:model-value="$emit('update:model-value', option.value)" />
+
diff --git a/es-ds-components/components/es-radio-button.vue b/es-ds-components/components/es-radio-button.vue
index b171a7aa4..355d3a2c5 100644
--- a/es-ds-components/components/es-radio-button.vue
+++ b/es-ds-components/components/es-radio-button.vue
@@ -1,40 +1,57 @@
-
-
+
-
-
- {{ props.displayName }}
-
-
+ :value="props.value"
+ :checked="isChecked"
+ @click="handleRadioButtonClick" />
+
+
+ {{ displayName }}
+
+
diff --git a/es-ds-components/nuxt.config.ts b/es-ds-components/nuxt.config.ts
index 3129776df..074e32957 100644
--- a/es-ds-components/nuxt.config.ts
+++ b/es-ds-components/nuxt.config.ts
@@ -48,6 +48,7 @@ export default defineNuxtConfig({
'@vuelidate/core',
'@vuelidate/validators',
'html-truncate',
+ '@vueuse/core',
'primevue/accordion',
'primevue/accordiontab',
'primevue/breadcrumb',
diff --git a/es-ds-components/package-lock.json b/es-ds-components/package-lock.json
index 9db216507..880ebb049 100644
--- a/es-ds-components/package-lock.json
+++ b/es-ds-components/package-lock.json
@@ -12,6 +12,7 @@
"@nuxtjs/google-fonts": "^3.2.0",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
+ "@vueuse/core": "^11.1.0",
"html-truncate": "^1.2.2",
"nuxt": "^3.13.1",
"nuxt-primevue": "^3.0.0",
@@ -2336,6 +2337,11 @@
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
},
+ "node_modules/@types/web-bluetooth": {
+ "version": "0.0.20",
+ "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
+ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
@@ -3499,6 +3505,89 @@
}
}
},
+ "node_modules/@vueuse/core": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.1.0.tgz",
+ "integrity": "sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.20",
+ "@vueuse/metadata": "11.1.0",
+ "@vueuse/shared": "11.1.0",
+ "vue-demi": ">=0.14.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/core/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vueuse/metadata": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.1.0.tgz",
+ "integrity": "sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.1.0.tgz",
+ "integrity": "sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==",
+ "dependencies": {
+ "vue-demi": ">=0.14.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
diff --git a/es-ds-components/package.json b/es-ds-components/package.json
index a3571e2f8..d448d3446 100644
--- a/es-ds-components/package.json
+++ b/es-ds-components/package.json
@@ -20,12 +20,13 @@
"@nuxtjs/google-fonts": "^3.2.0",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
+ "@vueuse/core": "^11.1.0",
+ "html-truncate": "^1.2.2",
"nuxt": "^3.13.1",
- "primevue": "^3.53.0",
"nuxt-primevue": "^3.0.0",
+ "primevue": "^3.53.0",
"prismjs": "^1.29.0",
- "vue": "^3.5.4",
- "html-truncate": "^1.2.2"
+ "vue": "^3.5.4"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
diff --git a/es-ds-docs/components/ds-molecules-list.vue b/es-ds-docs/components/ds-molecules-list.vue
index 995779eb7..3cbaad1d2 100644
--- a/es-ds-docs/components/ds-molecules-list.vue
+++ b/es-ds-docs/components/ds-molecules-list.vue
@@ -51,6 +51,9 @@
Radio button
+
+ Radio cards
+
Rating
diff --git a/es-ds-docs/package-lock.json b/es-ds-docs/package-lock.json
index 904325e6e..29b8c8e9c 100644
--- a/es-ds-docs/package-lock.json
+++ b/es-ds-docs/package-lock.json
@@ -14,6 +14,7 @@
"@nuxtjs/google-fonts": "^3.2.0",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
+ "@vueuse/core": "^11.1.0",
"clipboard": "^2.0.11",
"html-truncate": "^1.2.2",
"nuxt": "^3.13.1",
@@ -2393,6 +2394,11 @@
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
},
+ "node_modules/@types/web-bluetooth": {
+ "version": "0.0.20",
+ "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
+ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
@@ -3556,6 +3562,89 @@
}
}
},
+ "node_modules/@vueuse/core": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.1.0.tgz",
+ "integrity": "sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.20",
+ "@vueuse/metadata": "11.1.0",
+ "@vueuse/shared": "11.1.0",
+ "vue-demi": ">=0.14.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/core/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vueuse/metadata": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.1.0.tgz",
+ "integrity": "sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.1.0.tgz",
+ "integrity": "sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==",
+ "dependencies": {
+ "vue-demi": ">=0.14.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@yarnpkg/lockfile": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
diff --git a/es-ds-docs/package.json b/es-ds-docs/package.json
index 0436b2433..578c37b7a 100644
--- a/es-ds-docs/package.json
+++ b/es-ds-docs/package.json
@@ -23,13 +23,14 @@
"@nuxtjs/google-fonts": "^3.2.0",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
+ "@vueuse/core": "^11.1.0",
"clipboard": "^2.0.11",
+ "html-truncate": "^1.2.2",
"nuxt": "^3.13.1",
"nuxt-primevue": "^3.0.0",
"primevue": "^3.53.0",
"prismjs": "^1.29.0",
- "vue": "^3.5.4",
- "html-truncate": "^1.2.2"
+ "vue": "^3.5.4"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
diff --git a/es-ds-docs/pages/molecules/radio-button.vue b/es-ds-docs/pages/molecules/radio-button.vue
index 343b8e8c9..94cc77959 100644
--- a/es-ds-docs/pages/molecules/radio-button.vue
+++ b/es-ds-docs/pages/molecules/radio-button.vue
@@ -1,41 +1,39 @@
+
+
+
+
Radio cards
+
+
Radio card
+
+
+ {{ propertyTypeOptions[0].label }}
+
+
+
Default
+
+ Responsive font sizing is applied by default. This means the text will be smaller on mobile and larger on
+ desktop.
+
+
+
+
+ Selection:
+ {{ form.propertyType || '[none]' }}
+
+
+
+
Custom card styling
+
+ This example demonstrates customizing the content, spacing, and typography of the cards, as well as
+ changing the layout based on the breakpoint.
+
+
+
+
+ Selection:
+ {{ form.storageReason || '[none]' }}
+
+
+
+
Detached label
+
+ This example demonstrates a case where more control is needed over the position of the field label on the
+ desktop breakpoint, so it is rendered separately of the radio cards component. We do still need to pass the
+ label text into the radio cards component for accessibility purposes, but hide it visually by passing in a
+ prop.
+
+
+
+
+ Selection:
+ {{ form.installTimeline || '[none]' }}
+
+
+
+
Passing options
+
+
+
+ Selection:
+ {{ test2Selected || '[none]' }}
+
+
+
+
+
EsFormRadioCards props
+
+
+
+
+
EsFormRadioCard props
+
+
+
+
Migrating from ESDS 2.0 form radio cards
+
+ The previous
+
+ radio card
+
+ and
+ radio card group
+ components were based on
+ bootstrap-vue 's implementation.
+
+
+ Going forward the v-model
should be on the radio card group component when
+ passing in the options
prop.
+
+
+ When using the radio card group's default slot the v-model
should be on the
+ radio card components within that slot.
+
+
+
+
+
+
+
+
+
diff --git a/es-ds-styles/scss/_custom-forms.scss b/es-ds-styles/scss/_custom-forms.scss
index 8b795a825..72b1a0ac6 100644
--- a/es-ds-styles/scss/_custom-forms.scss
+++ b/es-ds-styles/scss/_custom-forms.scss
@@ -181,21 +181,21 @@
border-radius: variables.$custom-radio-indicator-border-radius;
}
- .custom-control-input:has(.custom-radio-input:checked) ~ .custom-control-label {
+ .custom-control-input:checked ~ .custom-control-label {
&::before {
background-color: variables.$custom-control-indicator-bg;
}
&::after {
- background-image: functions.escape-svg(variables.$custom-radio-indicator-icon-checked) !important;
+ background-image: functions.escape-svg(variables.$custom-radio-indicator-icon-checked);
}
}
- .custom-control-input:has(.custom-radio-input:disabled:checked) ~ .custom-control-label {
+ .custom-control-input:disabled:checked ~ .custom-control-label {
&::before {
@include gradients.gradient-bg(variables.$custom-control-indicator-checked-disabled-bg);
}
&::after {
- background-image: functions.escape-svg(variables.$custom-radio-indicator-icon-checked-disabled) !important;
+ background-image: functions.escape-svg(variables.$custom-radio-indicator-icon-checked-disabled);
}
}
}
diff --git a/es-ds-styles/scss/vue/components/_radio-cards.scss b/es-ds-styles/scss/vue/components/_radio-cards.scss
index c1147a314..47e43dfa3 100644
--- a/es-ds-styles/scss/vue/components/_radio-cards.scss
+++ b/es-ds-styles/scss/vue/components/_radio-cards.scss
@@ -148,6 +148,16 @@ $inner-circle-translate-y-from-top-desktop: $inner-circle-translate-x-from-left-
transform: variables.$btn-active-transform;
}
+ &.disabled {
+ background: variables.$gray-50;
+ border-color: variables.$gray-200;
+ color: variables.$gray-500;
+
+ &::before {
+ border: 2.5px solid variables.$gray-500;
+ }
+ }
+
input {
/**
* hide the radio button input elements the same way bootstrap vue does.