@davidbirchall/core 1.0.8 → 1.0.9

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.
Files changed (95) hide show
  1. package/dist/Button/types.d.ts +4 -0
  2. package/dist/Calendar/types.d.ts +22 -0
  3. package/{src/components/Card/types.ts → dist/Card/types.d.ts} +1 -1
  4. package/dist/Checkbox/types.d.ts +7 -0
  5. package/dist/DataTable/types.d.ts +11 -0
  6. package/dist/Dropdown/types.d.ts +13 -0
  7. package/dist/EmptyState/types.d.ts +8 -0
  8. package/dist/ErrorSummary/types.d.ts +4 -0
  9. package/dist/Heading/types.d.ts +6 -0
  10. package/dist/Input/types.d.ts +11 -0
  11. package/dist/Select/types.d.ts +15 -0
  12. package/dist/StatCard/types.d.ts +12 -0
  13. package/dist/Tag/types.d.ts +4 -0
  14. package/dist/TextArea/types.d.ts +11 -0
  15. package/dist/core.css +1 -0
  16. package/dist/core.js +24 -0
  17. package/dist/core.js.map +1 -0
  18. package/dist/core.umd.cjs +2 -0
  19. package/dist/core.umd.cjs.map +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/package.json +27 -0
  22. package/package.json +4 -1
  23. package/.storybook/main.ts +0 -18
  24. package/.storybook/preview.ts +0 -14
  25. package/src/components/Badge/Badge.stories.ts +0 -147
  26. package/src/components/Badge/Badge.test.ts +0 -57
  27. package/src/components/Badge/Badge.vue +0 -79
  28. package/src/components/Button/Button.stories.ts +0 -80
  29. package/src/components/Button/Button.test.ts +0 -145
  30. package/src/components/Button/Button.vue +0 -108
  31. package/src/components/Button/types.ts +0 -4
  32. package/src/components/Calendar/Calendar.stories.ts +0 -261
  33. package/src/components/Calendar/Calendar.test.ts +0 -119
  34. package/src/components/Calendar/Calendar.vue +0 -528
  35. package/src/components/Calendar/types.ts +0 -20
  36. package/src/components/Card/Card.stories.ts +0 -88
  37. package/src/components/Card/Card.test.ts +0 -173
  38. package/src/components/Card/Card.vue +0 -59
  39. package/src/components/Checkbox/Checkbox.stories.ts +0 -126
  40. package/src/components/Checkbox/Checkbox.test.ts +0 -155
  41. package/src/components/Checkbox/Checkbox.vue +0 -121
  42. package/src/components/Checkbox/types.ts +0 -7
  43. package/src/components/DataTable/DataTable.stories.ts +0 -156
  44. package/src/components/DataTable/DataTable.test.ts +0 -185
  45. package/src/components/DataTable/DataTable.vue +0 -177
  46. package/src/components/DataTable/types.ts +0 -12
  47. package/src/components/DatePicker/DatePicker.stories.ts +0 -172
  48. package/src/components/DatePicker/DatePicker.test.ts +0 -87
  49. package/src/components/DatePicker/DatePicker.vue +0 -302
  50. package/src/components/Dropdown/Dropdown.stories.ts +0 -231
  51. package/src/components/Dropdown/Dropdown.vue +0 -314
  52. package/src/components/Dropdown/types.ts +0 -14
  53. package/src/components/EmptyState/EmptyState.stories.ts +0 -189
  54. package/src/components/EmptyState/EmptyState.vue +0 -215
  55. package/src/components/EmptyState/types.ts +0 -8
  56. package/src/components/ErrorSummary/ErrorSummary.vue +0 -78
  57. package/src/components/ErrorSummary/types.ts +0 -4
  58. package/src/components/FormGroup/FormGroup.stories.ts +0 -264
  59. package/src/components/FormGroup/FormGroup.test.ts +0 -63
  60. package/src/components/FormGroup/FormGroup.vue +0 -58
  61. package/src/components/Heading/Heading.stories.ts +0 -121
  62. package/src/components/Heading/Heading.test.ts +0 -184
  63. package/src/components/Heading/Heading.vue +0 -95
  64. package/src/components/Heading/types.ts +0 -6
  65. package/src/components/Input/Input.stories.ts +0 -172
  66. package/src/components/Input/Input.test.ts +0 -213
  67. package/src/components/Input/Input.vue +0 -121
  68. package/src/components/Input/types.ts +0 -11
  69. package/src/components/Modal/Modal.stories.ts +0 -341
  70. package/src/components/Modal/Modal.test.ts +0 -99
  71. package/src/components/Modal/Modal.vue +0 -278
  72. package/src/components/ProgressBar/ProgressBar.stories.ts +0 -313
  73. package/src/components/ProgressBar/ProgressBar.test.ts +0 -98
  74. package/src/components/ProgressBar/ProgressBar.vue +0 -117
  75. package/src/components/Select/Select.stories.ts +0 -177
  76. package/src/components/Select/Select.test.ts +0 -225
  77. package/src/components/Select/Select.vue +0 -147
  78. package/src/components/Select/types.ts +0 -16
  79. package/src/components/StatCard/StatCard.stories.ts +0 -274
  80. package/src/components/StatCard/StatCard.vue +0 -226
  81. package/src/components/StatCard/types.ts +0 -12
  82. package/src/components/Tag/Tag.stories.ts +0 -78
  83. package/src/components/Tag/Tag.test.ts +0 -50
  84. package/src/components/Tag/Tag.vue +0 -71
  85. package/src/components/Tag/types.ts +0 -4
  86. package/src/components/TextArea/TextArea.stories.ts +0 -171
  87. package/src/components/TextArea/TextArea.test.ts +0 -202
  88. package/src/components/TextArea/TextArea.vue +0 -122
  89. package/src/components/TextArea/types.ts +0 -11
  90. package/src/components/index.ts +0 -5
  91. package/src/test/setup.ts +0 -1
  92. package/src/vite-env.d.ts +0 -6
  93. package/tsconfig.json +0 -29
  94. package/vite.config.ts +0 -33
  95. package/vitest.config.ts +0 -28
@@ -1,215 +0,0 @@
1
- <template>
2
- <div class="empty-state" :class="`empty-state--${size}`">
3
- <div v-if="icon || $slots.icon" class="empty-state__icon">
4
- <slot name="icon">
5
- <span class="empty-state__icon-text">{{ icon }}</span>
6
- </slot>
7
- </div>
8
-
9
- <div class="empty-state__content">
10
- <h3 v-if="title || $slots.title" class="empty-state__title">
11
- <slot name="title">{{ title }}</slot>
12
- </h3>
13
-
14
- <p v-if="description || $slots.description" class="empty-state__description">
15
- <slot name="description">{{ description }}</slot>
16
- </p>
17
-
18
- <div v-if="$slots.default" class="empty-state__body">
19
- <slot />
20
- </div>
21
- </div>
22
-
23
- <div v-if="actionText || $slots.action" class="empty-state__actions">
24
- <slot name="action">
25
- <button
26
- v-if="actionText"
27
- type="button"
28
- class="empty-state__button"
29
- :class="`empty-state__button--${actionVariant}`"
30
- @click="handleAction"
31
- >
32
- {{ actionText }}
33
- </button>
34
- </slot>
35
- </div>
36
- </div>
37
- </template>
38
-
39
- <script setup lang="ts">
40
-
41
- import type { EmptyStateProps } from './types'
42
-
43
- withDefaults(defineProps<EmptyStateProps>(), {
44
- title: '',
45
- description: '',
46
- icon: '',
47
- actionText: '',
48
- actionVariant: 'primary',
49
- size: 'medium'
50
- })
51
-
52
- const emit = defineEmits<{
53
- action: []
54
- }>()
55
-
56
- const handleAction = () => {
57
- emit('action')
58
- }
59
- </script>
60
-
61
- <style scoped>
62
- .empty-state {
63
- display: flex;
64
- flex-direction: column;
65
- align-items: center;
66
- justify-content: center;
67
- text-align: center;
68
- padding: 3rem 1.5rem;
69
- color: #6b7280;
70
- }
71
-
72
- .empty-state--small {
73
- padding: 1.5rem 1rem;
74
- }
75
-
76
- .empty-state--large {
77
- padding: 4rem 2rem;
78
- }
79
-
80
- .empty-state__icon {
81
- margin-bottom: 1.5rem;
82
- color: #9ca3af;
83
- }
84
-
85
- .empty-state--small .empty-state__icon {
86
- margin-bottom: 1rem;
87
- }
88
-
89
- .empty-state__icon-text {
90
- font-size: 3rem;
91
- line-height: 1;
92
- }
93
-
94
- .empty-state--small .empty-state__icon-text {
95
- font-size: 2rem;
96
- }
97
-
98
- .empty-state--large .empty-state__icon-text {
99
- font-size: 4rem;
100
- }
101
-
102
- .empty-state__content {
103
- max-width: 480px;
104
- margin-bottom: 1.5rem;
105
- }
106
-
107
- .empty-state--small .empty-state__content {
108
- max-width: 360px;
109
- margin-bottom: 1rem;
110
- }
111
-
112
- .empty-state--large .empty-state__content {
113
- max-width: 600px;
114
- margin-bottom: 2rem;
115
- }
116
-
117
- .empty-state__title {
118
- margin: 0 0 0.75rem 0;
119
- font-size: 1.25rem;
120
- font-weight: 600;
121
- color: #374151;
122
- }
123
-
124
- .empty-state--small .empty-state__title {
125
- font-size: 1rem;
126
- margin-bottom: 0.5rem;
127
- }
128
-
129
- .empty-state--large .empty-state__title {
130
- font-size: 1.5rem;
131
- margin-bottom: 1rem;
132
- }
133
-
134
- .empty-state__description {
135
- margin: 0;
136
- font-size: 0.875rem;
137
- line-height: 1.5;
138
- color: #6b7280;
139
- }
140
-
141
- .empty-state--small .empty-state__description {
142
- font-size: 0.8125rem;
143
- }
144
-
145
- .empty-state--large .empty-state__description {
146
- font-size: 1rem;
147
- }
148
-
149
- .empty-state__body {
150
- margin-top: 1rem;
151
- }
152
-
153
- .empty-state__actions {
154
- display: flex;
155
- gap: 0.75rem;
156
- flex-wrap: wrap;
157
- justify-content: center;
158
- }
159
-
160
- .empty-state__button {
161
- padding: 0.625rem 1.25rem;
162
- border: none;
163
- border-radius: 6px;
164
- font-size: 0.875rem;
165
- font-weight: 500;
166
- cursor: pointer;
167
- transition: all 0.2s;
168
- }
169
-
170
- .empty-state--small .empty-state__button {
171
- padding: 0.5rem 1rem;
172
- font-size: 0.8125rem;
173
- }
174
-
175
- .empty-state--large .empty-state__button {
176
- padding: 0.75rem 1.5rem;
177
- font-size: 1rem;
178
- }
179
-
180
- .empty-state__button--primary {
181
- background: #3b82f6;
182
- color: white;
183
- }
184
-
185
- .empty-state__button--primary:hover {
186
- background: #2563eb;
187
- transform: translateY(-1px);
188
- box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
189
- }
190
-
191
- .empty-state__button--primary:active {
192
- transform: translateY(0);
193
- }
194
-
195
- .empty-state__button--secondary {
196
- background: white;
197
- color: #374151;
198
- border: 1px solid #d1d5db;
199
- }
200
-
201
- .empty-state__button--secondary:hover {
202
- background: #f9fafb;
203
- border-color: #9ca3af;
204
- }
205
-
206
- @media (max-width: 768px) {
207
- .empty-state {
208
- padding: 2rem 1rem;
209
- }
210
-
211
- .empty-state--large {
212
- padding: 3rem 1.5rem;
213
- }
214
- }
215
- </style>
@@ -1,8 +0,0 @@
1
- export interface EmptyStateProps {
2
- title?: string
3
- description?: string
4
- icon?: string
5
- actionText?: string
6
- actionVariant?: 'primary' | 'secondary'
7
- size?: 'small' | 'medium' | 'large'
8
- }
@@ -1,78 +0,0 @@
1
- <template>
2
- <div v-if="errors.length" class="error-summary" role="alert" aria-live="assertive">
3
- <div class="error-summary__header">
4
- <span class="error-summary__icon">⚠️</span>
5
- <div>
6
- <h4 class="error-summary__title">{{ title }}</h4>
7
- <p class="error-summary__subtitle">Please fix the following {{ errors.length }} issue<span v-if="errors.length !== 1">s</span>:</p>
8
- </div>
9
- </div>
10
- <ul class="error-summary__list">
11
- <li v-for="error in errors" :key="error.id" class="error-summary__item">
12
- <a :href="`#${error.id}`" class="error-summary__link">
13
- {{ error.message }}
14
- </a>
15
- </li>
16
- </ul>
17
- </div>
18
- </template>
19
-
20
- <script setup lang="ts">
21
- import type { ErrorSummaryItem } from './types'
22
-
23
- withDefaults(defineProps<{
24
- title?: string
25
- errors: ErrorSummaryItem[]
26
- }>(), {
27
- title: 'There is a problem'
28
- })
29
- </script>
30
-
31
- <style scoped>
32
- .error-summary {
33
- border: 1px solid #fecaca;
34
- background: #fef2f2;
35
- border-radius: 0.75rem;
36
- padding: 1rem 1.25rem;
37
- margin-bottom: 1.25rem;
38
- }
39
-
40
- .error-summary__header {
41
- display: flex;
42
- gap: 0.75rem;
43
- align-items: flex-start;
44
- }
45
-
46
- .error-summary__icon {
47
- font-size: 1.25rem;
48
- }
49
-
50
- .error-summary__title {
51
- margin: 0;
52
- color: #991b1b;
53
- font-size: 1rem;
54
- font-weight: 700;
55
- }
56
-
57
- .error-summary__subtitle {
58
- margin: 0.25rem 0 0;
59
- color: #7f1d1d;
60
- font-size: 0.875rem;
61
- }
62
-
63
- .error-summary__list {
64
- margin: 0.75rem 0 0;
65
- padding-left: 1.25rem;
66
- color: #7f1d1d;
67
- }
68
-
69
- .error-summary__item + .error-summary__item {
70
- margin-top: 0.35rem;
71
- }
72
-
73
- .error-summary__link {
74
- color: #7f1d1d;
75
- text-decoration: underline;
76
- font-weight: 600;
77
- }
78
- </style>
@@ -1,4 +0,0 @@
1
- export type ErrorSummaryItem = {
2
- id: string
3
- message: string
4
- }
@@ -1,264 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
2
- import { ref } from 'vue'
3
- import FormGroup from './FormGroup.vue'
4
-
5
- const meta = {
6
- title: 'Components/FormGroup',
7
- component: FormGroup,
8
- tags: ['autodocs'],
9
- argTypes: {
10
- label: {
11
- control: 'text',
12
- description: 'Form field label'
13
- },
14
- id: {
15
- control: 'text',
16
- description: 'Form field ID'
17
- },
18
- hint: {
19
- control: 'text',
20
- description: 'Helper text'
21
- },
22
- error: {
23
- control: 'text',
24
- description: 'Error message'
25
- },
26
- required: {
27
- control: 'boolean',
28
- description: 'Whether field is required'
29
- }
30
- },
31
- args: {
32
- label: 'Field Label',
33
- id: 'field-id',
34
- required: false
35
- }
36
- } satisfies Meta<typeof FormGroup>
37
-
38
- export default meta
39
- type Story = StoryObj<typeof meta>
40
-
41
- export const Default: Story = {
42
- args: {
43
- label: 'Email Address',
44
- id: 'email',
45
- hint: 'We\'ll never share your email'
46
- },
47
- render: (args: any) => ({
48
- components: { FormGroup },
49
- setup() {
50
- return { args }
51
- },
52
- template: `
53
- <FormGroup v-bind="args">
54
- <input
55
- :id="args.id"
56
- type="email"
57
- placeholder="Enter your email"
58
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
59
- />
60
- </FormGroup>
61
- `
62
- })
63
- }
64
-
65
- export const Required: Story = {
66
- args: {
67
- label: 'Password',
68
- id: 'password',
69
- required: true,
70
- hint: 'Must be at least 8 characters'
71
- },
72
- render: (args: any) => ({
73
- components: { FormGroup },
74
- setup() {
75
- return { args }
76
- },
77
- template: `
78
- <FormGroup v-bind="args">
79
- <input
80
- :id="args.id"
81
- type="password"
82
- placeholder="Enter your password"
83
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
84
- />
85
- </FormGroup>
86
- `
87
- })
88
- }
89
-
90
- export const WithError: Story = {
91
- args: {
92
- label: 'Username',
93
- id: 'username',
94
- error: 'This username is already taken',
95
- required: true
96
- },
97
- render: (args: any) => ({
98
- components: { FormGroup },
99
- setup() {
100
- return { args }
101
- },
102
- template: `
103
- <FormGroup v-bind="args">
104
- <input
105
- :id="args.id"
106
- type="text"
107
- value="john_doe"
108
- placeholder="Enter username"
109
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
110
- />
111
- </FormGroup>
112
- `
113
- })
114
- }
115
-
116
- export const WithTextarea: Story = {
117
- args: {
118
- label: 'Description',
119
- id: 'description',
120
- hint: 'Brief description of your project'
121
- },
122
- render: (args: any) => ({
123
- components: { FormGroup },
124
- setup() {
125
- return { args }
126
- },
127
- template: `
128
- <FormGroup v-bind="args">
129
- <textarea
130
- :id="args.id"
131
- rows="4"
132
- placeholder="Enter description"
133
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-family: inherit;"
134
- />
135
- </FormGroup>
136
- `
137
- })
138
- }
139
-
140
- export const WithSelect: Story = {
141
- args: {
142
- label: 'Country',
143
- id: 'country',
144
- required: true
145
- },
146
- render: (args: any) => ({
147
- components: { FormGroup },
148
- setup() {
149
- return { args }
150
- },
151
- template: `
152
- <FormGroup v-bind="args">
153
- <select
154
- :id="args.id"
155
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
156
- >
157
- <option value="">Select a country</option>
158
- <option value="us">United States</option>
159
- <option value="uk">United Kingdom</option>
160
- <option value="ca">Canada</option>
161
- </select>
162
- </FormGroup>
163
- `
164
- })
165
- }
166
-
167
- export const CompleteForm: Story = {
168
- render: () => ({
169
- components: { FormGroup },
170
- setup() {
171
- const email = ref('')
172
- const password = ref('')
173
- const emailError = ref('')
174
-
175
- const validateEmail = () => {
176
- if (!email.value.includes('@')) {
177
- emailError.value = 'Please enter a valid email'
178
- } else {
179
- emailError.value = ''
180
- }
181
- }
182
-
183
- return { email, password, emailError, validateEmail }
184
- },
185
- template: `
186
- <form style="max-width: 400px;">
187
- <FormGroup
188
- label="Email"
189
- id="form-email"
190
- :error="emailError"
191
- required
192
- hint="Enter your email address"
193
- >
194
- <input
195
- id="form-email"
196
- v-model="email"
197
- @blur="validateEmail"
198
- type="email"
199
- placeholder="you@example.com"
200
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
201
- />
202
- </FormGroup>
203
-
204
- <FormGroup
205
- label="Password"
206
- id="form-password"
207
- required
208
- hint="Must be at least 8 characters"
209
- >
210
- <input
211
- id="form-password"
212
- v-model="password"
213
- type="password"
214
- placeholder="••••••••"
215
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
216
- />
217
- </FormGroup>
218
-
219
- <FormGroup
220
- label="Bio"
221
- id="form-bio"
222
- hint="Tell us a bit about yourself"
223
- >
224
- <textarea
225
- id="form-bio"
226
- rows="3"
227
- placeholder="I am..."
228
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-family: inherit;"
229
- />
230
- </FormGroup>
231
-
232
- <button
233
- type="submit"
234
- style="width: 100%; padding: 0.625rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; font-weight: 600; cursor: pointer;"
235
- >
236
- Submit
237
- </button>
238
- </form>
239
- `
240
- })
241
- }
242
-
243
- export const NoLabel: Story = {
244
- args: {
245
- id: 'search',
246
- hint: 'Search for anything...'
247
- },
248
- render: (args: any) => ({
249
- components: { FormGroup },
250
- setup() {
251
- return { args }
252
- },
253
- template: `
254
- <FormGroup v-bind="args">
255
- <input
256
- :id="args.id"
257
- type="search"
258
- placeholder="Search..."
259
- style="width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem;"
260
- />
261
- </FormGroup>
262
- `
263
- })
264
- }
@@ -1,63 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import FormGroup from './FormGroup.vue'
4
-
5
- describe('FormGroup', () => {
6
- it('renders label when provided', () => {
7
- const wrapper = mount(FormGroup, {
8
- props: {
9
- label: 'Email Address'
10
- }
11
- })
12
- expect(wrapper.find('.form-group__label').text()).toContain('Email Address')
13
- })
14
-
15
- it('shows required indicator when required prop is true', () => {
16
- const wrapper = mount(FormGroup, {
17
- props: {
18
- label: 'Name',
19
- required: true
20
- }
21
- })
22
- expect(wrapper.find('.form-group__required').exists()).toBe(true)
23
- })
24
-
25
- it('displays hint text when provided', () => {
26
- const wrapper = mount(FormGroup, {
27
- props: {
28
- hint: 'This is a helpful hint'
29
- }
30
- })
31
- expect(wrapper.find('.form-group__hint').text()).toBe('This is a helpful hint')
32
- })
33
-
34
- it('displays error message and adds error class', () => {
35
- const wrapper = mount(FormGroup, {
36
- props: {
37
- error: 'This field is required'
38
- }
39
- })
40
- expect(wrapper.find('.form-group__error').text()).toBe('This field is required')
41
- expect(wrapper.classes()).toContain('form-group--error')
42
- })
43
-
44
- it('hides hint when error is present', () => {
45
- const wrapper = mount(FormGroup, {
46
- props: {
47
- hint: 'Helpful hint',
48
- error: 'Error message'
49
- }
50
- })
51
- expect(wrapper.find('.form-group__hint').exists()).toBe(false)
52
- expect(wrapper.find('.form-group__error').exists()).toBe(true)
53
- })
54
-
55
- it('renders slot content', () => {
56
- const wrapper = mount(FormGroup, {
57
- slots: {
58
- default: '<input type="text" />'
59
- }
60
- })
61
- expect(wrapper.find('input').exists()).toBe(true)
62
- })
63
- })
@@ -1,58 +0,0 @@
1
- <template>
2
- <div :class="['form-group', { 'form-group--error': error }]">
3
- <label v-if="label" :for="id" class="form-group__label">
4
- {{ label }}
5
- </label>
6
- <slot />
7
- <small v-if="hint && !error" class="form-group__hint">{{ hint }}</small>
8
- <small v-if="error" class="form-group__error">{{ error }}</small>
9
- </div>
10
- </template>
11
-
12
- <script setup lang="ts">
13
- interface Props {
14
- label?: string
15
- id?: string
16
- hint?: string
17
- error?: string
18
- required?: boolean
19
- }
20
-
21
- withDefaults(defineProps<Props>(), {
22
- label: '',
23
- id: '',
24
- hint: '',
25
- error: '',
26
- required: false
27
- })
28
- </script>
29
-
30
- <style scoped>
31
- .form-group {
32
- margin-bottom: 1.5rem;
33
- }
34
-
35
- .form-group__label {
36
- display: block;
37
- font-weight: 600;
38
- margin-bottom: 0.5rem;
39
- color: #374151;
40
- font-size: 0.9375rem;
41
- }
42
-
43
-
44
- .form-group__hint {
45
- display: block;
46
- margin-top: 0.25rem;
47
- color: #6b7280;
48
- font-size: 0.875rem;
49
- }
50
-
51
- .form-group__error {
52
- display: block;
53
- margin-top: 0.25rem;
54
- color: #dc2626;
55
- font-size: 0.875rem;
56
- font-weight: 500;
57
- }
58
- </style>