@urbicon-ui/blocks 6.37.1 → 6.37.2

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.
@@ -1,17 +1,23 @@
1
+ import { FIELD_DISABLED_FRAME, FIELD_LABEL, FIELD_LABEL_DISABLED, FIELD_MESSAGE_TONES, FIELD_NATIVE_DISABLED, FIELD_NATIVE_READONLY, FIELD_REQUIRED_LABEL, FIELD_SURFACE, FIELD_TRANSITION, fieldErrorFrame, fieldFocusRing, fieldIntentFrames, fieldSurfaceVariants } from '../../internal/field-chrome.js';
1
2
  import { tv } from '../../utils/variants.js';
3
+ // Each cell is directly focusable, so the ring lives on the cell itself.
4
+ const focus = 'focus-visible';
5
+ const surface = fieldSurfaceVariants(focus);
6
+ const intents = fieldIntentFrames(focus);
2
7
  export const pinInputVariants = tv({
3
8
  slots: {
4
9
  root: ['flex flex-col gap-1.5'],
5
- label: ['block font-medium text-text-secondary text-sm'],
10
+ label: [FIELD_LABEL],
6
11
  group: ['flex items-center'],
7
12
  cell: [
8
13
  'box-border text-center font-medium tabular-nums caret-primary',
9
- 'border text-text-primary bg-surface-base',
10
- 'transition-[color,background-color,border-color,box-shadow] duration-[var(--blocks-duration-fast)] ease-out',
11
- 'focus-visible:outline-none focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:z-10',
14
+ FIELD_SURFACE,
15
+ FIELD_TRANSITION,
16
+ // `focus-visible:z-10` lifts the focused cell's ring above its neighbours.
17
+ `focus-visible:outline-none ${fieldFocusRing(focus)} focus-visible:z-10`,
12
18
  'hover:border-border-default',
13
- 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-subtle',
14
- 'read-only:bg-surface-subtle read-only:cursor-default'
19
+ FIELD_NATIVE_DISABLED,
20
+ FIELD_NATIVE_READONLY
15
21
  ],
16
22
  separator: ['text-text-tertiary select-none'],
17
23
  message: ['text-xs']
@@ -24,13 +30,9 @@ export const pinInputVariants = tv({
24
30
  commit: { cell: 'rounded-commit' }
25
31
  },
26
32
  variant: {
27
- outlined: { cell: 'border-border-subtle' },
28
- filled: {
29
- cell: 'bg-surface-interactive border-transparent hover:bg-surface-hover focus-visible:bg-surface-base'
30
- },
31
- ghost: {
32
- cell: 'bg-transparent hover:bg-surface-subtle focus-visible:bg-surface-base focus-visible:border-border-subtle'
33
- }
33
+ outlined: { cell: surface.outlined },
34
+ filled: { cell: surface.filled },
35
+ ghost: { cell: surface.ghost }
34
36
  },
35
37
  size: {
36
38
  sm: {
@@ -53,20 +55,14 @@ export const pinInputVariants = tv({
53
55
  },
54
56
  intent: {
55
57
  default: {},
56
- success: {
57
- cell: 'border-success focus-visible:border-success focus-visible:ring-success/20'
58
- },
59
- warning: {
60
- cell: 'border-warning focus-visible:border-warning focus-visible:ring-warning/20'
61
- },
62
- danger: {
63
- cell: 'border-danger focus-visible:border-danger focus-visible:ring-danger/20'
64
- }
58
+ success: { cell: intents.success },
59
+ warning: { cell: intents.warning },
60
+ danger: { cell: intents.danger }
65
61
  },
66
62
  disabled: {
67
63
  true: {
68
- cell: 'opacity-50 cursor-not-allowed bg-surface-disabled pointer-events-none',
69
- label: 'text-text-disabled'
64
+ cell: FIELD_DISABLED_FRAME,
65
+ label: FIELD_LABEL_DISABLED
70
66
  }
71
67
  },
72
68
  readonly: {
@@ -75,17 +71,17 @@ export const pinInputVariants = tv({
75
71
  // Declared BEFORE `error` so the error tone wins the message-color bucket
76
72
  // in every call shape — `{ error: true }` alone must read red.
77
73
  messageType: {
78
- error: { message: 'text-danger' },
79
- helper: { message: 'text-text-tertiary' }
74
+ error: { message: FIELD_MESSAGE_TONES.error },
75
+ helper: { message: FIELD_MESSAGE_TONES.helper }
80
76
  },
81
77
  error: {
82
78
  true: {
83
- cell: 'border-danger focus-visible:border-danger focus-visible:ring-danger/20',
84
- message: 'text-danger'
79
+ cell: fieldErrorFrame(focus),
80
+ message: FIELD_MESSAGE_TONES.error
85
81
  }
86
82
  },
87
83
  required: {
88
- true: { label: "after:content-['*'] after:ml-1 after:text-danger" }
84
+ true: { label: FIELD_REQUIRED_LABEL }
89
85
  }
90
86
  },
91
87
  compoundVariants: [
@@ -1,17 +1,23 @@
1
+ import { FIELD_DISABLED_FRAME, FIELD_LABEL, FIELD_LABEL_DISABLED, FIELD_MESSAGE_TONES, FIELD_REQUIRED_LABEL, FIELD_SURFACE, FIELD_TRANSITION, fieldErrorFrame, fieldFocusRing, fieldIntentFrames, fieldSurfaceVariants } from '../../internal/field-chrome.js';
1
2
  import { tv } from '../../utils/variants.js';
3
+ // Borderless segments live inside a bordered container, so the ring lights the
4
+ // whole field via focus-within rather than focus-visible on one element.
5
+ const focus = 'focus-within';
6
+ const surface = fieldSurfaceVariants(focus);
7
+ const intents = fieldIntentFrames(focus);
2
8
  export const timeInputVariants = tv({
3
9
  slots: {
4
10
  wrapper: ['flex flex-col gap-1.5 w-full'],
5
- label: ['block font-medium text-text-secondary text-sm'],
11
+ label: [FIELD_LABEL],
6
12
  // The bordered container reads as a single field; the segment inputs inside
7
13
  // are borderless. The focus ring lives here via focus-within so tabbing
8
14
  // between segments keeps the whole field lit.
9
15
  field: [
10
16
  'inline-flex items-center box-border w-fit',
11
- 'border text-text-primary bg-surface-base',
12
- 'transition-[color,background-color,border-color,box-shadow] duration-[var(--blocks-duration-fast)] ease-out',
17
+ FIELD_SURFACE,
18
+ FIELD_TRANSITION,
13
19
  'hover:border-border-default',
14
- 'focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20'
20
+ fieldFocusRing(focus)
15
21
  ],
16
22
  icon: ['pointer-events-none inline-flex items-center justify-center text-text-tertiary'],
17
23
  segment: [
@@ -38,13 +44,9 @@ export const timeInputVariants = tv({
38
44
  commit: { field: 'rounded-commit' }
39
45
  },
40
46
  variant: {
41
- outlined: { field: 'border-border-subtle' },
42
- filled: {
43
- field: 'bg-surface-interactive border-transparent hover:bg-surface-hover focus-within:bg-surface-base'
44
- },
45
- ghost: {
46
- field: 'bg-transparent hover:bg-surface-subtle focus-within:bg-surface-base focus-within:border-border-subtle'
47
- }
47
+ outlined: { field: surface.outlined },
48
+ filled: { field: surface.filled },
49
+ ghost: { field: surface.ghost }
48
50
  },
49
51
  size: {
50
52
  sm: {
@@ -69,31 +71,32 @@ export const timeInputVariants = tv({
69
71
  },
70
72
  intent: {
71
73
  default: {},
72
- success: { field: 'border-success focus-within:border-success focus-within:ring-success/20' },
73
- warning: { field: 'border-warning focus-within:border-warning focus-within:ring-warning/20' },
74
- danger: { field: 'border-danger focus-within:border-danger focus-within:ring-danger/20' }
74
+ success: { field: intents.success },
75
+ warning: { field: intents.warning },
76
+ danger: { field: intents.danger }
75
77
  },
76
78
  disabled: {
77
79
  true: {
78
- field: 'opacity-50 cursor-not-allowed bg-surface-disabled pointer-events-none',
79
- label: 'text-text-disabled'
80
+ field: FIELD_DISABLED_FRAME,
81
+ label: FIELD_LABEL_DISABLED
80
82
  }
81
83
  },
82
84
  readonly: {
85
+ // No `cursor-default` here — the container's segments carry their own cursor.
83
86
  true: { field: 'bg-surface-subtle' }
84
87
  },
85
88
  messageType: {
86
- error: { message: 'text-danger' },
87
- helper: { message: 'text-text-tertiary' }
89
+ error: { message: FIELD_MESSAGE_TONES.error },
90
+ helper: { message: FIELD_MESSAGE_TONES.helper }
88
91
  },
89
92
  error: {
90
93
  true: {
91
- field: 'border-danger focus-within:border-danger focus-within:ring-danger/20',
92
- message: 'text-danger'
94
+ field: fieldErrorFrame(focus),
95
+ message: FIELD_MESSAGE_TONES.error
93
96
  }
94
97
  },
95
98
  required: {
96
- true: { label: "after:content-['*'] after:ml-1 after:text-danger" }
99
+ true: { label: FIELD_REQUIRED_LABEL }
97
100
  },
98
101
  fullWidth: {
99
102
  true: { field: 'w-full justify-start' }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Shared style fragments for the bordered field frame.
3
+ *
4
+ * Input, PinInput and TimeInput (and NumberInput, via its Input composition)
5
+ * all render the same field chrome — the neutral resting frame, the focus ring,
6
+ * the `outlined | filled | ghost` surfaces, the intent colours, the disabled /
7
+ * error states and the label / message scaffolding. Before this module each of
8
+ * the three `*.variants.ts` hand-copied those class strings, so a token fix had
9
+ * to be applied in three places and drift crept in silently.
10
+ *
11
+ * These exports are the single source for the shared tokens. They are composed
12
+ * back into each `tv()` config at the EXACT positions the inlined strings held,
13
+ * so the resolved (flattened) class output is byte-identical — proven by the
14
+ * render-identity matrix diff in the debt-fix-wave-5 (40 960 Input / 2 304 Pin /
15
+ * 4 608 TimeInput combinations, zero diff). This is a pure de-duplication, not a
16
+ * behaviour change.
17
+ *
18
+ * The one axis of genuine divergence is the focus mechanism: Input and PinInput
19
+ * put the ring on the focusable element itself (`focus-visible:`), while
20
+ * TimeInput hosts borderless segments inside a bordered container and lights the
21
+ * whole field via `focus-within:`. That is why the ring / variant / intent /
22
+ * error fragments are parameterised by {@link FieldFocus} rather than fixed.
23
+ *
24
+ * NOT covered here (deliberately component-local): the per-component frame
25
+ * prefix (Input's `w-full`, PinInput's cell content styles, TimeInput's
26
+ * `inline-flex` container), Input's `underline` variant and `placeholder`
27
+ * colour, PinInput's `focus-visible:z-10`, TimeInput's `fullWidth` and its
28
+ * cursor-free readonly. The shared label/message MARKUP in the `.svelte` files
29
+ * is a separate, larger cut (see technical-debt "Field chrome…" part b).
30
+ *
31
+ * This is not a component and imports nothing from other component dirs, so it
32
+ * is exempt from the cross-component import guard (imports-lint treats
33
+ * `internal/**` as the extraction target).
34
+ */
35
+ /** The field's colour/border/shadow transition — the longest shared string. */
36
+ export declare const FIELD_TRANSITION = "transition-[color,background-color,border-color,box-shadow] duration-[var(--blocks-duration-fast)] ease-out";
37
+ /** The neutral resting frame: border + primary text + base surface. Input
38
+ * appends its own `placeholder:` colour after this. */
39
+ export declare const FIELD_SURFACE = "border text-text-primary bg-surface-base";
40
+ /** `:disabled` / `:read-only` pseudo-class fallbacks on a NATIVE field element
41
+ * (Input's `base`, PinInput's `cell`). TimeInput's frame is a container whose
42
+ * segments carry their own states, so it omits these. */
43
+ export declare const FIELD_NATIVE_DISABLED = "disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-subtle";
44
+ export declare const FIELD_NATIVE_READONLY = "read-only:bg-surface-subtle read-only:cursor-default";
45
+ /**
46
+ * Where the focus ring lives: `focus-visible:` on a directly-focusable field
47
+ * (Input, PinInput cell), `focus-within:` on a container that hosts focusable
48
+ * segments (TimeInput).
49
+ */
50
+ export type FieldFocus = 'focus-visible' | 'focus-within';
51
+ /** The primary focus ring (border + 2px ring at 20% alpha), for the given mode. */
52
+ export declare const fieldFocusRing: (f: FieldFocus) => string;
53
+ /**
54
+ * The `outlined | filled | ghost` surface values, parameterised by focus mode.
55
+ * `outlined` is a constant; consumers add their own extra variants (Input's
56
+ * `underline`) alongside.
57
+ */
58
+ export declare const fieldSurfaceVariants: (f: FieldFocus) => {
59
+ outlined: string;
60
+ filled: string;
61
+ ghost: string;
62
+ };
63
+ /**
64
+ * The intent frame values (border + focus ring tint) for success/warning/danger,
65
+ * parameterised by focus mode. `default` carries no frame class, so callers keep
66
+ * their own empty `default: {}` slot entry.
67
+ */
68
+ export declare const fieldIntentFrames: (f: FieldFocus) => {
69
+ success: string;
70
+ warning: string;
71
+ danger: string;
72
+ };
73
+ /** The error frame value (danger border + ring), parameterised by focus mode. */
74
+ export declare const fieldErrorFrame: (f: FieldFocus) => string;
75
+ /** Frame classes when the whole control is disabled (the `disabled` variant). */
76
+ export declare const FIELD_DISABLED_FRAME = "opacity-50 cursor-not-allowed bg-surface-disabled pointer-events-none";
77
+ /** The field label base, shared verbatim across all three fields. */
78
+ export declare const FIELD_LABEL = "block font-medium text-text-secondary text-sm";
79
+ /** Label colour when the control is disabled. */
80
+ export declare const FIELD_LABEL_DISABLED = "text-text-disabled";
81
+ /** Required-marker asterisk appended after the label. */
82
+ export declare const FIELD_REQUIRED_LABEL = "after:content-['*'] after:ml-1 after:text-danger";
83
+ /** The two `messageType` message tones (error wins the fold when both apply). */
84
+ export declare const FIELD_MESSAGE_TONES: {
85
+ readonly error: "text-danger";
86
+ readonly helper: "text-text-tertiary";
87
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Shared style fragments for the bordered field frame.
3
+ *
4
+ * Input, PinInput and TimeInput (and NumberInput, via its Input composition)
5
+ * all render the same field chrome — the neutral resting frame, the focus ring,
6
+ * the `outlined | filled | ghost` surfaces, the intent colours, the disabled /
7
+ * error states and the label / message scaffolding. Before this module each of
8
+ * the three `*.variants.ts` hand-copied those class strings, so a token fix had
9
+ * to be applied in three places and drift crept in silently.
10
+ *
11
+ * These exports are the single source for the shared tokens. They are composed
12
+ * back into each `tv()` config at the EXACT positions the inlined strings held,
13
+ * so the resolved (flattened) class output is byte-identical — proven by the
14
+ * render-identity matrix diff in the debt-fix-wave-5 (40 960 Input / 2 304 Pin /
15
+ * 4 608 TimeInput combinations, zero diff). This is a pure de-duplication, not a
16
+ * behaviour change.
17
+ *
18
+ * The one axis of genuine divergence is the focus mechanism: Input and PinInput
19
+ * put the ring on the focusable element itself (`focus-visible:`), while
20
+ * TimeInput hosts borderless segments inside a bordered container and lights the
21
+ * whole field via `focus-within:`. That is why the ring / variant / intent /
22
+ * error fragments are parameterised by {@link FieldFocus} rather than fixed.
23
+ *
24
+ * NOT covered here (deliberately component-local): the per-component frame
25
+ * prefix (Input's `w-full`, PinInput's cell content styles, TimeInput's
26
+ * `inline-flex` container), Input's `underline` variant and `placeholder`
27
+ * colour, PinInput's `focus-visible:z-10`, TimeInput's `fullWidth` and its
28
+ * cursor-free readonly. The shared label/message MARKUP in the `.svelte` files
29
+ * is a separate, larger cut (see technical-debt "Field chrome…" part b).
30
+ *
31
+ * This is not a component and imports nothing from other component dirs, so it
32
+ * is exempt from the cross-component import guard (imports-lint treats
33
+ * `internal/**` as the extraction target).
34
+ */
35
+ /** The field's colour/border/shadow transition — the longest shared string. */
36
+ export const FIELD_TRANSITION = 'transition-[color,background-color,border-color,box-shadow] duration-[var(--blocks-duration-fast)] ease-out';
37
+ /** The neutral resting frame: border + primary text + base surface. Input
38
+ * appends its own `placeholder:` colour after this. */
39
+ export const FIELD_SURFACE = 'border text-text-primary bg-surface-base';
40
+ /** `:disabled` / `:read-only` pseudo-class fallbacks on a NATIVE field element
41
+ * (Input's `base`, PinInput's `cell`). TimeInput's frame is a container whose
42
+ * segments carry their own states, so it omits these. */
43
+ export const FIELD_NATIVE_DISABLED = 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-subtle';
44
+ export const FIELD_NATIVE_READONLY = 'read-only:bg-surface-subtle read-only:cursor-default';
45
+ /** The primary focus ring (border + 2px ring at 20% alpha), for the given mode. */
46
+ export const fieldFocusRing = (f) => `${f}:border-primary ${f}:ring-2 ${f}:ring-primary/20`;
47
+ /**
48
+ * The `outlined | filled | ghost` surface values, parameterised by focus mode.
49
+ * `outlined` is a constant; consumers add their own extra variants (Input's
50
+ * `underline`) alongside.
51
+ */
52
+ export const fieldSurfaceVariants = (f) => ({
53
+ outlined: 'border-border-subtle',
54
+ filled: `bg-surface-interactive border-transparent hover:bg-surface-hover ${f}:bg-surface-base`,
55
+ ghost: `bg-transparent hover:bg-surface-subtle ${f}:bg-surface-base ${f}:border-border-subtle`
56
+ });
57
+ /**
58
+ * The intent frame values (border + focus ring tint) for success/warning/danger,
59
+ * parameterised by focus mode. `default` carries no frame class, so callers keep
60
+ * their own empty `default: {}` slot entry.
61
+ */
62
+ export const fieldIntentFrames = (f) => ({
63
+ success: `border-success ${f}:border-success ${f}:ring-success/20`,
64
+ warning: `border-warning ${f}:border-warning ${f}:ring-warning/20`,
65
+ danger: `border-danger ${f}:border-danger ${f}:ring-danger/20`
66
+ });
67
+ /** The error frame value (danger border + ring), parameterised by focus mode. */
68
+ export const fieldErrorFrame = (f) => `border-danger ${f}:border-danger ${f}:ring-danger/20`;
69
+ /** Frame classes when the whole control is disabled (the `disabled` variant). */
70
+ export const FIELD_DISABLED_FRAME = 'opacity-50 cursor-not-allowed bg-surface-disabled pointer-events-none';
71
+ /** The field label base, shared verbatim across all three fields. */
72
+ export const FIELD_LABEL = 'block font-medium text-text-secondary text-sm';
73
+ /** Label colour when the control is disabled. */
74
+ export const FIELD_LABEL_DISABLED = 'text-text-disabled';
75
+ /** Required-marker asterisk appended after the label. */
76
+ export const FIELD_REQUIRED_LABEL = "after:content-['*'] after:ml-1 after:text-danger";
77
+ /** The two `messageType` message tones (error wins the fold when both apply). */
78
+ export const FIELD_MESSAGE_TONES = {
79
+ error: 'text-danger',
80
+ helper: 'text-text-tertiary'
81
+ };
@@ -1,18 +1,23 @@
1
+ import { FIELD_DISABLED_FRAME, FIELD_LABEL, FIELD_LABEL_DISABLED, FIELD_MESSAGE_TONES, FIELD_NATIVE_DISABLED, FIELD_NATIVE_READONLY, FIELD_REQUIRED_LABEL, FIELD_SURFACE, FIELD_TRANSITION, fieldErrorFrame, fieldFocusRing, fieldIntentFrames, fieldSurfaceVariants } from '../../internal/field-chrome.js';
1
2
  import { tv } from '../../utils/variants.js';
3
+ // Input and PinInput light the focusable element itself; TimeInput its container.
4
+ const focus = 'focus-visible';
5
+ const surface = fieldSurfaceVariants(focus);
6
+ const intents = fieldIntentFrames(focus);
2
7
  export const inputVariants = tv({
3
8
  slots: {
4
9
  wrapper: ['flex flex-col w-full gap-1.5'],
5
10
  container: ['relative flex items-center'],
6
11
  base: [
7
- 'w-full box-border transition-[color,background-color,border-color,box-shadow] duration-[var(--blocks-duration-fast)] ease-out',
12
+ `w-full box-border ${FIELD_TRANSITION}`,
8
13
  // Radius driven by `tier` axis below.
9
14
  'focus-visible:outline-none',
10
- 'border text-text-primary bg-surface-base placeholder:text-text-quaternary',
11
- 'hover:border-border-default focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20',
12
- 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-subtle',
13
- 'read-only:bg-surface-subtle read-only:cursor-default'
15
+ `${FIELD_SURFACE} placeholder:text-text-quaternary`,
16
+ `hover:border-border-default ${fieldFocusRing(focus)}`,
17
+ FIELD_NATIVE_DISABLED,
18
+ FIELD_NATIVE_READONLY
14
19
  ],
15
- label: ['block font-medium text-text-secondary text-sm'],
20
+ label: [FIELD_LABEL],
16
21
  message: ['text-xs mt-1.5'],
17
22
  iconContainer: [
18
23
  'absolute top-0 bottom-0 flex items-center justify-center z-10 pointer-events-none'
@@ -40,13 +45,13 @@ export const inputVariants = tv({
40
45
  },
41
46
  variant: {
42
47
  outlined: {
43
- base: 'border-border-subtle'
48
+ base: surface.outlined
44
49
  },
45
50
  filled: {
46
- base: 'bg-surface-interactive border-transparent hover:bg-surface-hover focus-visible:bg-surface-base'
51
+ base: surface.filled
47
52
  },
48
53
  ghost: {
49
- base: 'bg-transparent hover:bg-surface-subtle focus-visible:bg-surface-base focus-visible:border-border-subtle'
54
+ base: surface.ghost
50
55
  },
51
56
  underline: {
52
57
  base: 'bg-transparent border-0 border-b-2 border-border-subtle rounded-none focus-visible:ring-0'
@@ -94,19 +99,19 @@ export const inputVariants = tv({
94
99
  intent: {
95
100
  default: {},
96
101
  success: {
97
- base: 'border-success focus-visible:border-success focus-visible:ring-success/20'
102
+ base: intents.success
98
103
  },
99
104
  warning: {
100
- base: 'border-warning focus-visible:border-warning focus-visible:ring-warning/20'
105
+ base: intents.warning
101
106
  },
102
107
  danger: {
103
- base: 'border-danger focus-visible:border-danger focus-visible:ring-danger/20'
108
+ base: intents.danger
104
109
  }
105
110
  },
106
111
  disabled: {
107
112
  true: {
108
- base: 'opacity-50 cursor-not-allowed bg-surface-disabled pointer-events-none',
109
- label: 'text-text-disabled'
113
+ base: FIELD_DISABLED_FRAME,
114
+ label: FIELD_LABEL_DISABLED
110
115
  }
111
116
  },
112
117
  readonly: {
@@ -117,18 +122,18 @@ export const inputVariants = tv({
117
122
  // Declared BEFORE `error` so the error tone wins the message-color
118
123
  // bucket in every call shape — `{ error: true }` alone must read red.
119
124
  messageType: {
120
- error: { message: 'text-danger' },
121
- helper: { message: 'text-text-tertiary' }
125
+ error: { message: FIELD_MESSAGE_TONES.error },
126
+ helper: { message: FIELD_MESSAGE_TONES.helper }
122
127
  },
123
128
  error: {
124
129
  true: {
125
- base: 'border-danger focus-visible:border-danger focus-visible:ring-danger/20',
126
- message: 'text-danger'
130
+ base: fieldErrorFrame(focus),
131
+ message: FIELD_MESSAGE_TONES.error
127
132
  }
128
133
  },
129
134
  required: {
130
135
  true: {
131
- label: "after:content-['*'] after:ml-1 after:text-danger"
136
+ label: FIELD_REQUIRED_LABEL
132
137
  }
133
138
  },
134
139
  hasLeftIcon: { true: {} },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/blocks",
3
- "version": "6.37.1",
3
+ "version": "6.37.2",
4
4
  "description": "Svelte 5 UI component library with Tailwind CSS 4, OKLCH design tokens and zero runtime dependencies",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -93,8 +93,8 @@
93
93
  "@sveltejs/package": "^2.5.8",
94
94
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
95
95
  "@tailwindcss/vite": "^4.3.1",
96
- "@urbicon-ui/i18n": "6.37.1",
97
- "@urbicon-ui/shared-types": "6.37.1",
96
+ "@urbicon-ui/i18n": "6.37.2",
97
+ "@urbicon-ui/shared-types": "6.37.2",
98
98
  "prettier": "^3.8.4",
99
99
  "prettier-plugin-svelte": "^4.1.1",
100
100
  "prettier-plugin-tailwindcss": "^0.8.0",