+
+ {this.$slots.default?.()}
+
+
+ );
+ }
+});
diff --git a/devui/radio/src/radio.scss b/devui/radio/src/radio.scss
new file mode 100644
index 00000000..4cf62e7a
--- /dev/null
+++ b/devui/radio/src/radio.scss
@@ -0,0 +1,136 @@
+@import '../../style/theme/color';
+@import '../../style/core/_font';
+
+.devui-radio {
+ &.disabled {
+ cursor: not-allowed;
+
+ .devui-radio-label {
+ color: $devui-disabled-text;
+ }
+ }
+
+ font-size: $devui-font-size;
+ line-height: 1.5;
+ font-weight: normal;
+ cursor: pointer;
+ color: $devui-text;
+ margin: 0 auto;
+
+ svg {
+ .devui-outer {
+ stroke: $devui-line;
+ fill: transparent;
+
+ &.disabled {
+ stroke: $devui-disabled-line;
+ fill: $devui-disabled-bg;
+ }
+ }
+
+ .devui-inner {
+ fill: $devui-icon-fill-active;
+ }
+ }
+
+ &:hover:not(.disabled) {
+ svg .devui-outer {
+ stroke: $devui-form-control-line-active;
+ }
+
+ .devui-radio-label {
+ color: $devui-primary-hover;
+ }
+ }
+
+ &:active:not(.disabled),
+ &:focus:not(.disabled) {
+ svg .devui-outer {
+ stroke: $devui-form-control-line-active;
+ }
+ }
+
+ &.active {
+ svg {
+ .devui-outer {
+ opacity: 1;
+ stroke: $devui-form-control-line-active;
+ transition: stroke 50ms cubic-bezier(0.755, 0.05, 0.855, 0.06);
+
+ &.disabled {
+ stroke: $devui-icon-fill-active-disabled;
+ fill: transparent;
+ }
+ }
+
+ .devui-inner {
+ opacity: 1;
+
+ &.disabled {
+ fill: $devui-icon-fill-active-disabled;
+ }
+
+ transform: scale(1);
+ transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1), opacity 200ms cubic-bezier(0.23, 1, 0.32, 1);
+ }
+ }
+
+ &:not(.disabled) {
+ &:active,
+ &:focus,
+ &:hover {
+ .devui-radio-material {
+ svg {
+ .devui-outer {
+ stroke: $devui-form-control-line-active-hover;
+ }
+
+ .devui-inner {
+ fill: $devui-icon-fill-active-hover;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .devui-outer {
+ opacity: 1;
+ transition: stroke 50ms cubic-bezier(0.755, 0.05, 0.855, 0.06);
+ }
+
+ .devui-inner {
+ opacity: 0;
+ transform: scale(0);
+ transform-origin: 50% 50%;
+ transition: transform 200ms cubic-bezier(0.755, 0.05, 0.855, 0.06), opacity 200ms cubic-bezier(0.755, 0.05, 0.855, 0.06);
+ }
+
+ .devui-radio-material {
+ vertical-align: middle;
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ height: 16px;
+ width: 16px;
+ user-select: none;
+ transform: translateY(-1px);
+ }
+
+ .devui-radio-label {
+ color: $devui-text;
+ margin-left: 8px;
+ font-size: $devui-font-size;
+ transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+ }
+
+ .devui-radio-input {
+ opacity: 0;
+ z-index: -1;
+ width: 0;
+ height: 0;
+ display: none;
+ overflow: hidden;
+ pointer-events: none;
+ }
+}
diff --git a/devui/radio/src/radio.tsx b/devui/radio/src/radio.tsx
new file mode 100644
index 00000000..e325c91a
--- /dev/null
+++ b/devui/radio/src/radio.tsx
@@ -0,0 +1,86 @@
+import { defineComponent, ExtractPropTypes, inject, computed } from 'vue';
+import { radioProps, radioGroupInjectionKey } from './use-radio';
+import './radio.scss';
+
+export default defineComponent({
+ name: 'DRadio',
+ props: radioProps,
+ emits: ['change', 'update:checked'],
+ setup(props: ExtractPropTypes