@appscode/design-system 2.17.87 → 2.17.91-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appscode/design-system",
3
- "version": "2.17.87",
3
+ "version": "2.17.91-alpha.1",
4
4
  "description": "A design system for Appscode websites and dashboards made using Bulma",
5
5
  "main": "main.scss",
6
6
  "scripts": {
@@ -16,6 +16,7 @@ import AcBreadcrumb from "./v3/breadcrumbs/Breadcrumb.vue";
16
16
  import AcButton from "./v3/button/Button.vue";
17
17
  import AcButtons from "./v3/button/Buttons.vue";
18
18
  import AcDownloadBtn from "./v3/button/DownloadBtn.vue";
19
+ import AcSegmentedControl from "./v3/button/SegmentedControl.vue";
19
20
 
20
21
  import AcCard from "./v3/cards/Card.vue";
21
22
  import AcCardContent from "./v3/cards/CardContent.vue";
@@ -23,6 +24,8 @@ import AcCardHeader from "./v3/cards/CardHeader.vue";
23
24
  import AcCards from "../design/cards/Cards.vue";
24
25
  import AcCluster from "./v3/cards/Cluster.vue";
25
26
  import AcCounter from "./v3/cards/Counter.vue";
27
+ import AcDetailCard from "./v3/cards/DetailCard.vue";
28
+ import AcTableCard from "./v3/cards/TableCard.vue";
26
29
  import AcFeatureCard from "./v3/cards/FeatureCard.vue";
27
30
  import AcFeatureCards from "./v3/cards/FeatureCards.vue";
28
31
  import AcInfoCard from "./v3/cards/InfoCard.vue";
@@ -31,9 +34,14 @@ import AcOrgCard from "./v3/cards/OrgCard.vue";
31
34
  import AcOverviewCard from "./v3/cards/OverviewCard.vue";
32
35
  import AcOverviewCards from "./v3/cards/OverviewCards.vue";
33
36
  import AcPayment from "./v3/cards/Payment.vue";
37
+ import AcRadioCard from "./v3/cards/RadioCard.vue";
34
38
  import AcSessionCard from "./v3/cards/SessionCard.vue";
39
+ import AcStatCard from "./v3/cards/StatCard.vue";
35
40
  import AcVendor from "./v3/cards/Vendor.vue";
36
41
 
42
+ import AcRecoverableWindow from "./v3/charts/RecoverableWindow.vue";
43
+ import AcTimelineTrack from "./v3/charts/TimelineTrack.vue";
44
+
37
45
  import AcCodeBlock from "./v3/code-preview/CodeBlock.vue";
38
46
  import AcCodeGroup from "./v3/code-preview/CodeGroup.vue";
39
47
 
@@ -192,6 +200,9 @@ import AcTags from "./v3/tag/Tags.vue";
192
200
 
193
201
  import AcSimpleSelect from "./v3/form-fields/SimpleSelect.vue";
194
202
 
203
+ export * from "./v3/charts/types";
204
+ export { defaultRangeOptions } from "./v3/charts/timeline";
205
+
195
206
  export {
196
207
  AcAccordion,
197
208
  AcAlert,
@@ -204,13 +215,19 @@ export {
204
215
  AcBreadcrumb,
205
216
  AcButton,
206
217
  AcButtons,
218
+ AcRadioCard,
219
+ AcRecoverableWindow,
220
+ AcTimelineTrack,
207
221
  AcDownloadBtn,
222
+ AcSegmentedControl,
208
223
  AcCard,
209
224
  AcCardContent,
210
225
  AcCardHeader,
211
226
  AcCards,
212
227
  AcCluster,
213
228
  AcCounter,
229
+ AcDetailCard,
230
+ AcTableCard,
214
231
  AcFeatureCard,
215
232
  AcFeatureCards,
216
233
  AcInfoCard,
@@ -220,6 +237,7 @@ export {
220
237
  AcOverviewCards,
221
238
  AcPayment,
222
239
  AcSessionCard,
240
+ AcStatCard,
223
241
  AcVendor,
224
242
  AcCodeBlock,
225
243
  AcCodeGroup,
@@ -27,3 +27,25 @@
27
27
  gap: #{$i}px;
28
28
  }
29
29
  }
30
+
31
+ // Responsive grid utilities (mobile-first, Bulma breakpoints)
32
+ // e.g. "grid grid-cols-1 grid-cols-2-tablet grid-cols-4-desktop"
33
+ $grid-breakpoints: (
34
+ "tablet": 769px,
35
+ "desktop": 1024px,
36
+ "widescreen": 1216px,
37
+ "fullhd": 1408px,
38
+ );
39
+
40
+ @each $bp, $min in $grid-breakpoints {
41
+ @media screen and (min-width: $min) {
42
+ @for $i from 1 through 12 {
43
+ .grid-cols-#{$i}-#{$bp} {
44
+ grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
45
+ }
46
+ .col-span-#{$i}-#{$bp} {
47
+ grid-column: span #{$i} / span #{$i};
48
+ }
49
+ }
50
+ }
51
+ }
@@ -158,6 +158,36 @@ strong {
158
158
  font-weight: $weight-bold;
159
159
  }
160
160
 
161
+ // Letter spacing utilities
162
+ $letter-spacings: (
163
+ "tighter": -0.05em,
164
+ "tight": -0.025em,
165
+ "normal": 0,
166
+ "wide": 0.025em,
167
+ "wider": 0.05em,
168
+ "widest": 0.1em,
169
+ );
170
+ @each $key, $value in $letter-spacings {
171
+ .tracking-#{$key} {
172
+ letter-spacing: $value;
173
+ }
174
+ }
175
+
176
+ // Line height utilities
177
+ $line-heights: (
178
+ "none": 1,
179
+ "tight": 1.25,
180
+ "snug": 1.375,
181
+ "normal": 1.5,
182
+ "relaxed": 1.625,
183
+ "loose": 2,
184
+ );
185
+ @each $key, $value in $line-heights {
186
+ .leading-#{$key} {
187
+ line-height: $value;
188
+ }
189
+ }
190
+
161
191
  .material-icons {
162
192
  font-size: 1em;
163
193
  }
@@ -1,4 +1,4 @@
1
- @mixin badge($text-color, $bg-color, $text-dark, $bg-light) {
1
+ @mixin badge($text-color, $bg-color, $text-dark, $bg-light, $border-light) {
2
2
  color: $text-color;
3
3
  background-color: $bg-color;
4
4
  .circle {
@@ -7,6 +7,8 @@
7
7
  &.is-light {
8
8
  background-color: $bg-light;
9
9
  color: $text-dark;
10
+ border: 1px solid $border-light;
11
+ padding: 3px 7px; // keep same size as borderless tags
10
12
  .circle {
11
13
  background-color: $bg-color;
12
14
  }
@@ -48,25 +50,25 @@
48
50
  }
49
51
 
50
52
  &.is-primary {
51
- @include badge($white-100, $ac-primary, $primary-10, $primary-95);
53
+ @include badge($white-100, $ac-primary, $primary-10, $primary-95, $primary-80);
52
54
  }
53
55
  &.is-secondary {
54
- @include badge($white-100, $ac-secondary, $secondary-10, $secondary-95);
56
+ @include badge($white-100, $ac-secondary, $secondary-10, $secondary-95, $secondary-80);
55
57
  }
56
58
  &.is-info {
57
- @include badge($white-100, $info, $blue-10, $blue-95);
59
+ @include badge($white-100, $info, $blue-10, $blue-95, $blue-80);
58
60
  }
59
61
  &.is-success {
60
- @include badge($white-100, $success, $green-10, $green-95);
62
+ @include badge($white-100, $success, $green-10, $green-95, $green-80);
61
63
  }
62
64
  &.is-warning {
63
- @include badge($white-100, $warning, $yellow-10, $yellow-95);
65
+ @include badge($white-100, $warning, $yellow-10, $yellow-95, $yellow-80);
64
66
  }
65
67
  &.is-danger {
66
- @include badge($white-100, $danger, $red-10, $red-95);
68
+ @include badge($white-100, $danger, $red-10, $red-95, $red-80);
67
69
  }
68
70
  &.is-dark {
69
- @include badge($white-100, $gray, $gray-10, $gray-95);
71
+ @include badge($white-100, $gray, $gray-10, $gray-95, $gray-80);
70
72
  }
71
73
 
72
74
  .circle {
@@ -0,0 +1,15 @@
1
+ $rw-pitr-start: #059669;
2
+ $rw-pitr-end: #34d399;
3
+ $rw-pitr-legend: #10b981;
4
+ $rw-snapshot: #00994a;
5
+ $rw-base-divider: #166534;
6
+ $rw-failed: #dc2626;
7
+ $rw-scheduled: #9ca3af;
8
+ $rw-running: #6ee7b7;
9
+ $rw-rail: #f3f4f6;
10
+ $rw-rail-border: #e5e7eb;
11
+ $rw-axis-text: #9ca3af;
12
+ $rw-guide: rgba(255, 255, 255, 0.35);
13
+ $rw-dot-success: #10b981;
14
+ $rw-dot-warning: #f97316;
15
+ $rw-dot-danger: #dc2626;
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ import { ref } from "vue";
3
+ import AcButton from "./Button.vue";
4
+
5
+ const props = withDefaults(defineProps<{ editable?: boolean }>(), {
6
+ editable: false,
7
+ });
8
+
9
+ const options = defineModel<string[]>("options", { required: true });
10
+ const model = defineModel<string>();
11
+
12
+ const editingIndex = ref(-1);
13
+ const draft = ref("");
14
+
15
+ const vFocus = {
16
+ mounted: (el: HTMLInputElement) => {
17
+ el.focus();
18
+ el.select();
19
+ },
20
+ };
21
+
22
+ function onClick(index: number) {
23
+ if (props.editable && options.value[index] === model.value) {
24
+ draft.value = options.value[index];
25
+ editingIndex.value = index;
26
+ } else {
27
+ model.value = options.value[index];
28
+ }
29
+ }
30
+
31
+ function save() {
32
+ const index = editingIndex.value;
33
+ const value = draft.value.trim();
34
+ editingIndex.value = -1;
35
+ if (index === -1 || !value || options.value.includes(value)) return;
36
+ options.value = options.value.map((option, i) => (i === index ? value : option));
37
+ model.value = value;
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <div class="buttons has-addons has-gap mb-0">
43
+ <template v-for="(option, index) in options" :key="index">
44
+ <input
45
+ v-if="index === editingIndex"
46
+ v-model="draft"
47
+ v-focus
48
+ class="input height-24 width-48 py-0 has-text-centered"
49
+ @keydown.enter.prevent="save"
50
+ @keydown.esc="editingIndex = -1"
51
+ @blur="save"
52
+ />
53
+ <AcButton
54
+ v-else
55
+ type="button"
56
+ :title="option"
57
+ :tooltip="editable && option === model ? 'Click to edit' : undefined"
58
+ :aria-pressed="option === model"
59
+ :modifier-classes="option === model ? 'is-tinny has-text-weight-bold' : 'is-tinny has-text-grey'"
60
+ @click="onClick(index)"
61
+ />
62
+ </template>
63
+ </div>
64
+ </template>
@@ -0,0 +1,201 @@
1
+ <script setup lang="ts">
2
+ import { computed, watch } from "vue";
3
+ import CheckIcon from "../icons/CheckIcon.vue";
4
+
5
+ interface Props {
6
+ // the value the user has to type to confirm
7
+ name: string;
8
+ // text around the highlighted name: "{prefix} <name> {suffix}"
9
+ prefix?: string;
10
+ suffix?: string;
11
+ required?: boolean;
12
+ placeholder?: string;
13
+ confirmedText?: string;
14
+ // warning note under the input; hidden when empty
15
+ hint?: string;
16
+ caseSensitive?: boolean;
17
+ // ignore leading/trailing whitespace while comparing
18
+ trim?: boolean;
19
+ disabled?: boolean;
20
+ id?: string;
21
+ modifierClasses?: string;
22
+ }
23
+
24
+ const props = withDefaults(defineProps<Props>(), {
25
+ prefix: "Type",
26
+ suffix: "to confirm",
27
+ required: true,
28
+ placeholder: "",
29
+ confirmedText: "Identity Confirmed",
30
+ hint: "",
31
+ caseSensitive: true,
32
+ trim: false,
33
+ disabled: false,
34
+ id: "",
35
+ modifierClasses: "",
36
+ });
37
+
38
+ const emit = defineEmits<{
39
+ confirmed: [isConfirmed: boolean];
40
+ }>();
41
+
42
+ // the typed value
43
+ const input = defineModel<string>({ default: "" });
44
+
45
+ const inputId = computed(() => props.id || `confirm-name-${props.name.toLowerCase().replace(/\s+/g, "-")}`);
46
+
47
+ const normalize = (value: string) => {
48
+ const v = props.trim ? value.trim() : value;
49
+ return props.caseSensitive ? v : v.toLowerCase();
50
+ };
51
+
52
+ const isConfirmed = computed(() => !!props.name && normalize(input.value) === normalize(props.name));
53
+
54
+ watch(isConfirmed, (value) => emit("confirmed", value), { immediate: true });
55
+
56
+ defineExpose({ isConfirmed });
57
+ </script>
58
+
59
+ <template>
60
+ <div class="confirm-name-card" :class="[modifierClasses, { 'is-confirmed': isConfirmed, 'is-disabled': disabled }]">
61
+ <label :for="inputId" class="cnc-label">
62
+ {{ prefix }} <code class="cnc-name">{{ name }}</code> {{ suffix }}
63
+ <span v-if="required" class="cnc-required">*</span>
64
+ </label>
65
+
66
+ <div class="cnc-field is-flex is-align-items-center is-flex-wrap-wrap gap-16">
67
+ <input
68
+ :id="inputId"
69
+ v-model="input"
70
+ type="text"
71
+ class="cnc-input"
72
+ autocomplete="off"
73
+ autocapitalize="off"
74
+ spellcheck="false"
75
+ :placeholder="placeholder || name"
76
+ :required="required"
77
+ :disabled="disabled"
78
+ :aria-invalid="!!input && !isConfirmed"
79
+ />
80
+ <span v-if="isConfirmed" class="cnc-badge" role="status">
81
+ <CheckIcon class="cnc-badge-icon" />
82
+ {{ confirmedText }}
83
+ </span>
84
+ </div>
85
+
86
+ <p v-if="hint || $slots.hint" class="cnc-hint is-flex is-align-items-center gap-8 mb-0">
87
+ <svg class="cnc-hint-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true">
88
+ <path
89
+ fill="none"
90
+ stroke="currentColor"
91
+ stroke-linecap="round"
92
+ stroke-linejoin="round"
93
+ stroke-width="2"
94
+ d="M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"
95
+ />
96
+ </svg>
97
+ <slot name="hint">{{ hint }}</slot>
98
+ </p>
99
+ </div>
100
+ </template>
101
+
102
+ <style scoped lang="scss">
103
+ .confirm-name-card {
104
+ --cnc-mono: "Inconsolata", monospace;
105
+
106
+ padding: 24px 28px;
107
+ border: 1px solid $red-95;
108
+ border-radius: 12px;
109
+ background-color: $red-97;
110
+
111
+ &.is-disabled {
112
+ opacity: 0.6;
113
+ }
114
+ }
115
+
116
+ .cnc-label {
117
+ display: block;
118
+ margin-bottom: 16px;
119
+ font-size: 15px;
120
+ font-weight: 600;
121
+ line-height: 1.8;
122
+ color: $color-heading;
123
+ }
124
+
125
+ .cnc-name {
126
+ padding: 2px 8px;
127
+ border: 1px solid $red-90;
128
+ border-radius: 6px;
129
+ font-family: var(--cnc-mono);
130
+ font-size: 14px;
131
+ font-weight: 700;
132
+ color: $danger;
133
+ background-color: $red-95;
134
+ }
135
+
136
+ .cnc-required {
137
+ color: $danger;
138
+ }
139
+
140
+ .cnc-input {
141
+ width: 100%;
142
+ max-width: 384px;
143
+ height: 44px;
144
+ padding: 0 16px;
145
+ border: 1px solid $color-border;
146
+ border-radius: 8px;
147
+ font-family: var(--cnc-mono);
148
+ font-size: 15px;
149
+ color: $color-heading;
150
+ background-color: $white-100;
151
+ outline: none;
152
+ transition: border-color 0.2s;
153
+
154
+ &:focus {
155
+ border-color: $color-border-dark;
156
+ }
157
+
158
+ &[aria-invalid="true"] {
159
+ border-color: $red-80;
160
+ }
161
+
162
+ .is-confirmed & {
163
+ border-color: $green-80;
164
+ }
165
+
166
+ &:disabled {
167
+ cursor: not-allowed;
168
+ }
169
+ }
170
+
171
+ .cnc-badge {
172
+ display: inline-flex;
173
+ align-items: center;
174
+ gap: 6px;
175
+ padding: 6px 12px;
176
+ border: 1px solid $green-80;
177
+ border-radius: 4px;
178
+ font-size: 14px;
179
+ font-weight: 600;
180
+ color: $success;
181
+ background-color: $green-95;
182
+
183
+ .cnc-badge-icon {
184
+ width: 16px;
185
+ height: 16px;
186
+ }
187
+ }
188
+
189
+ .cnc-hint {
190
+ margin-top: 16px;
191
+ font-size: 13px;
192
+ color: $color-label;
193
+
194
+ .cnc-hint-icon {
195
+ flex-shrink: 0;
196
+ width: 18px;
197
+ height: 18px;
198
+ color: $yellow-40;
199
+ }
200
+ }
201
+ </style>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import AcDivider from "../divider/Divider.vue";
3
+
4
+ interface Field {
5
+ label: string;
6
+ value?: string | number;
7
+ key?: string;
8
+ }
9
+
10
+ withDefaults(
11
+ defineProps<{
12
+ title: string;
13
+ fields?: Field[];
14
+ columns?: number;
15
+ modifierClasses?: string;
16
+ }>(),
17
+ {
18
+ fields: () => [],
19
+ columns: 2,
20
+ modifierClasses: "",
21
+ },
22
+ );
23
+ </script>
24
+
25
+ <template>
26
+ <div class="is-flex is-flex-direction-column p-20 b-1 is-rounded-8 bg-white" :class="modifierClasses">
27
+ <div class="is-flex is-justify-content-space-between is-align-items-center gap-8">
28
+ <div class="is-flex is-align-items-center gap-12">
29
+ <span v-if="$slots.icon" class="icon is-medium is-flex-shrink-0 is-rounded-8 bg-primary-93 has-text-primary">
30
+ <slot name="icon" />
31
+ </span>
32
+ <h5>{{ title }}</h5>
33
+ </div>
34
+ <slot name="header-right" />
35
+ </div>
36
+
37
+ <AcDivider />
38
+
39
+ <div class="is-flex-grow-1">
40
+ <div v-if="fields.length" class="grid gap-16" :class="`grid-cols-${columns}`">
41
+ <div v-for="field in fields" :key="field.label">
42
+ <p class="has-text-grey">{{ field.label }}</p>
43
+ <div class="is-flex is-flex-wrap-wrap is-align-items-center gap-8 has-text-weight-semibold">
44
+ <slot :name="`field-${field.key}`" :field="field">{{ field.value }}</slot>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ <slot />
49
+ </div>
50
+
51
+ <template v-if="$slots.footer">
52
+ <AcDivider />
53
+ <div class="is-flex is-justify-content-space-between is-align-items-center gap-8">
54
+ <slot name="footer" />
55
+ </div>
56
+ </template>
57
+ </div>
58
+ </template>
@@ -0,0 +1,162 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ interface Props {
5
+ value: string | number;
6
+ label: string;
7
+ name?: string;
8
+ description?: string;
9
+ footer?: string;
10
+ status?: string;
11
+ statusTone?: "success" | "warning" | "danger";
12
+ disabled?: boolean;
13
+ id?: string;
14
+ modifierClasses?: string;
15
+ }
16
+
17
+ const props = withDefaults(defineProps<Props>(), {
18
+ name: "radio-card",
19
+ description: "",
20
+ footer: "",
21
+ status: "",
22
+ statusTone: "success",
23
+ disabled: false,
24
+ id: "",
25
+ modifierClasses: "",
26
+ });
27
+
28
+ const selected = defineModel<string | number>();
29
+
30
+ const inputId = computed(() => props.id || `${props.name}-${props.value}`);
31
+ const isSelected = computed(() => selected.value === props.value);
32
+
33
+ function select() {
34
+ if (props.disabled) return;
35
+ selected.value = props.value;
36
+ }
37
+ </script>
38
+
39
+ <template>
40
+ <div
41
+ class="radio-card ac-radio"
42
+ :class="[modifierClasses, { 'is-selected': isSelected, 'is-disabled': disabled }]"
43
+ @click="select"
44
+ >
45
+ <input :id="inputId" v-model="selected" type="radio" :name="name" :value="value" :disabled="disabled" />
46
+ <label :for="inputId">{{ label }}</label>
47
+
48
+ <p v-if="description || $slots.default" class="radio-card__description">
49
+ <slot>{{ description }}</slot>
50
+ </p>
51
+
52
+ <p v-if="status || $slots.status" class="radio-card__status" :class="`radio-card__status--${statusTone}`">
53
+ <slot name="status">
54
+ <span class="radio-card__dot" />
55
+ {{ status }}
56
+ </slot>
57
+ </p>
58
+
59
+ <p v-else-if="footer || $slots.footer" class="radio-card__footer">
60
+ <slot name="footer">{{ footer }}</slot>
61
+ </p>
62
+ </div>
63
+ </template>
64
+
65
+ <style lang="scss" scoped>
66
+ .radio-card {
67
+ display: flex;
68
+ flex-direction: column;
69
+ padding: 16px;
70
+ border: 1px solid $color-border;
71
+ border-radius: 10px;
72
+ background-color: $white-100;
73
+ cursor: pointer;
74
+ transition: 0.2s ease-in-out;
75
+
76
+ label {
77
+ font-size: 14px;
78
+ font-weight: 600;
79
+ color: $slate-10;
80
+ padding-left: 32px;
81
+ line-height: 20px;
82
+
83
+ &::before {
84
+ width: 20px;
85
+ height: 20px;
86
+ border-color: $color-border-dark;
87
+ }
88
+
89
+ &::after {
90
+ top: 5px;
91
+ left: 5px;
92
+ width: 10px;
93
+ height: 10px;
94
+ }
95
+ }
96
+
97
+ &:hover:not(.is-disabled) {
98
+ border-color: $ac-primary;
99
+ }
100
+
101
+ &.is-selected {
102
+ border-color: $ac-primary;
103
+ background-color: $primary-97;
104
+ box-shadow: inset 0 0 0 1px $ac-primary;
105
+ }
106
+
107
+ &.is-disabled {
108
+ cursor: not-allowed;
109
+ opacity: 0.6;
110
+ }
111
+ }
112
+
113
+ .radio-card__description {
114
+ margin: 6px 0 0;
115
+ padding-left: 32px;
116
+ font-size: 14px;
117
+ line-height: 20px;
118
+ color: $slate-40;
119
+ }
120
+
121
+ .radio-card__footer,
122
+ .radio-card__status {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 6px;
126
+ margin: 16px 0 0;
127
+ padding-top: 0;
128
+ font-size: 12px;
129
+ line-height: 16px;
130
+ }
131
+
132
+ .radio-card__footer {
133
+ margin-top: auto;
134
+ padding-top: 16px;
135
+ color: $slate-50;
136
+ }
137
+
138
+ .radio-card__status {
139
+ margin-top: auto;
140
+ padding-top: 16px;
141
+
142
+ &--success {
143
+ color: $ac-primary;
144
+ }
145
+
146
+ &--warning {
147
+ color: $warning;
148
+ }
149
+
150
+ &--danger {
151
+ color: $danger;
152
+ }
153
+ }
154
+
155
+ .radio-card__dot {
156
+ width: 6px;
157
+ height: 6px;
158
+ border-radius: 50%;
159
+ background-color: currentColor;
160
+ flex-shrink: 0;
161
+ }
162
+ </style>