@streamscloud/kit 0.9.6 → 0.9.8

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.
@@ -37,8 +37,11 @@ whole card becomes a clickable target with hover and focus state, no `interactiv
37
37
  | `--sc-kit--card--color` | Body text color | `var(--sc-kit--color--text--primary)` |
38
38
  | `--sc-kit--card--header--background` | Header bar background | `transparent` |
39
39
  | `--sc-kit--card--header--border-color` | Header bottom divider | `var(--sc-kit--color--border)` |
40
+ | `--sc-kit--card--header--padding` | Header padding (full shorthand) | block `var(--sc-kit--space--3)`, inline follows the `padding` preset |
41
+ | `--sc-kit--card--body--padding` | Body padding (full shorthand) | the `padding` preset value |
40
42
  | `--sc-kit--card--footer--background` | Footer bar background | `var(--sc-kit--color--bg--element)` |
41
43
  | `--sc-kit--card--footer--border-color` | Footer top divider | `var(--sc-kit--color--border)` |
44
+ | `--sc-kit--card--footer--padding` | Footer padding (full shorthand) | block `var(--sc-kit--space--3)`, inline follows the `padding` preset |
42
45
  -->
43
46
 
44
47
  <style>.card {
@@ -53,6 +56,9 @@ whole card becomes a clickable target with hover and focus state, no `interactiv
53
56
  --_card--header-border-color: var(--sc-kit--card--header--border-color, var(--sc-kit--color--border));
54
57
  --_card--footer-background: var(--sc-kit--card--footer--background, var(--sc-kit--color--bg--element));
55
58
  --_card--footer-border-color: var(--sc-kit--card--footer--border-color, var(--sc-kit--color--border));
59
+ --_card--header-padding: var(--sc-kit--card--header--padding, var(--sc-kit--space--3) var(--_card--chrome-padding-inline));
60
+ --_card--body-padding: var(--sc-kit--card--body--padding, var(--_card--body-padding-default));
61
+ --_card--footer-padding: var(--sc-kit--card--footer--padding, var(--sc-kit--space--3) var(--_card--chrome-padding-inline));
56
62
  display: block;
57
63
  width: 100%;
58
64
  box-sizing: border-box;
@@ -66,25 +72,32 @@ whole card becomes a clickable target with hover and focus state, no `interactiv
66
72
  .card--raised {
67
73
  box-shadow: var(--_card--shadow);
68
74
  }
69
- .card--pad-none .card__body {
70
- padding: 0;
75
+ .card--pad-none {
76
+ --_card--body-padding-default: 0;
77
+ --_card--chrome-padding-inline: var(--sc-kit--space--5);
71
78
  }
72
- .card--pad-sm .card__body {
73
- padding: var(--sc-kit--space--3);
79
+ .card--pad-sm {
80
+ --_card--body-padding-default: var(--sc-kit--space--3);
81
+ --_card--chrome-padding-inline: var(--sc-kit--space--3);
74
82
  }
75
- .card--pad-md .card__body {
76
- padding: var(--sc-kit--space--5);
83
+ .card--pad-md {
84
+ --_card--body-padding-default: var(--sc-kit--space--5);
85
+ --_card--chrome-padding-inline: var(--sc-kit--space--5);
77
86
  }
78
- .card--pad-lg .card__body {
79
- padding: var(--sc-kit--space--6);
87
+ .card--pad-lg {
88
+ --_card--body-padding-default: var(--sc-kit--space--6);
89
+ --_card--chrome-padding-inline: var(--sc-kit--space--6);
90
+ }
91
+ .card__body {
92
+ padding: var(--_card--body-padding);
80
93
  }
81
94
  .card__header {
82
- padding: var(--sc-kit--space--3) var(--sc-kit--space--5);
95
+ padding: var(--_card--header-padding);
83
96
  background: var(--_card--header-background);
84
97
  border-bottom: 1px solid var(--_card--header-border-color);
85
98
  }
86
99
  .card__footer {
87
- padding: var(--sc-kit--space--3) var(--sc-kit--space--5);
100
+ padding: var(--_card--footer-padding);
88
101
  background: var(--_card--footer-background);
89
102
  border-top: 1px solid var(--_card--footer-border-color);
90
103
  }
@@ -4,7 +4,7 @@ type CardPadding = 'none' | 'sm' | 'md' | 'lg';
4
4
  type Props = {
5
5
  /** Visual elevation. @default 'flat' */
6
6
  elevation?: CardElevation;
7
- /** Inner body padding preset. Header and footer have their own fixed chrome. @default 'md' */
7
+ /** Inner body padding preset. Header/footer inline padding follows it; their block padding stays fixed. @default 'md' */
8
8
  padding?: CardPadding;
9
9
  header?: Snippet;
10
10
  footer?: Snippet;
@@ -34,8 +34,11 @@ type Props = {
34
34
  * | `--sc-kit--card--color` | Body text color | `var(--sc-kit--color--text--primary)` |
35
35
  * | `--sc-kit--card--header--background` | Header bar background | `transparent` |
36
36
  * | `--sc-kit--card--header--border-color` | Header bottom divider | `var(--sc-kit--color--border)` |
37
+ * | `--sc-kit--card--header--padding` | Header padding (full shorthand) | block `var(--sc-kit--space--3)`, inline follows the `padding` preset |
38
+ * | `--sc-kit--card--body--padding` | Body padding (full shorthand) | the `padding` preset value |
37
39
  * | `--sc-kit--card--footer--background` | Footer bar background | `var(--sc-kit--color--bg--element)` |
38
40
  * | `--sc-kit--card--footer--border-color` | Footer top divider | `var(--sc-kit--color--border)` |
41
+ * | `--sc-kit--card--footer--padding` | Footer padding (full shorthand) | block `var(--sc-kit--space--3)`, inline follows the `padding` preset |
39
42
  */
40
43
  declare const Cmp: import("svelte").Component<Props, {}, "">;
41
44
  type Cmp = ReturnType<typeof Cmp>;
@@ -288,7 +288,7 @@ DatePicker — single-day calendar picker built on Floating UI for positioning.
288
288
  font-family: inherit;
289
289
  font-size: var(--_dp--font-size);
290
290
  line-height: var(--sc-kit--leading--tight);
291
- box-shadow: inset 0 -0.13em var(--_dp--underline-color);
291
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_dp--underline-color);
292
292
  cursor: pointer;
293
293
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
294
294
  }
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" generics="T extends Record<string, unknown>, K extends keyof T & string">import { Validatable } from '../validatable';
2
2
  import { default as FormField } from './cmp.form-field.svelte';
3
- let { handler, name, label, required = false, validateOn, debounceMs, disableTouchedTracking, on, children } = $props();
3
+ let { handler, name, label, required = false, validateOn, debounceMs, disableTouchedTracking, reserveErrorSpace, on, children } = $props();
4
4
  </script>
5
5
 
6
6
  <FormField label={label} required={required}>
@@ -10,6 +10,7 @@ let { handler, name, label, required = false, validateOn, debounceMs, disableTou
10
10
  validateOn={validateOn}
11
11
  debounceMs={debounceMs}
12
12
  disableTouchedTracking={disableTouchedTracking}
13
+ reserveErrorSpace={reserveErrorSpace}
13
14
  on={on}
14
15
  children={children} />
15
16
  </FormField>
@@ -17,6 +17,8 @@ declare function $$render<T extends Record<string, unknown>, K extends keyof T &
17
17
  debounceMs?: number;
18
18
  /** Show errors before the field has been touched. Forwarded to `<Validatable>`. @default false */
19
19
  disableTouchedTracking?: boolean;
20
+ /** Reserve space below the field so a toggling error doesn't shift layout. Forwarded to `<Validatable>`. @default true */
21
+ reserveErrorSpace?: boolean;
20
22
  /** Extra consumer callbacks. Forwarded to `<Validatable>`. */
21
23
  on?: {
22
24
  input?: (value: T[K]) => void;
@@ -216,7 +216,7 @@ or the clear button (see `HandleInput`'s availability pill).
216
216
  content: "";
217
217
  position: absolute;
218
218
  inset: auto 0 0 0;
219
- height: 0.13em;
219
+ height: max(2px, 0.125em);
220
220
  background: var(--_in--underline-color);
221
221
  pointer-events: none;
222
222
  z-index: 1;
@@ -245,7 +245,7 @@ requires a non-null value. CSS API mirrors `Input` for visual-language compatibi
245
245
  font-size: var(--_ni--font-size);
246
246
  line-height: var(--sc-kit--leading--tight);
247
247
  cursor: text;
248
- box-shadow: inset 0 -0.13em var(--_ni--underline-color);
248
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_ni--underline-color);
249
249
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
250
250
  }
251
251
  .numeral-input--sm {
@@ -212,7 +212,7 @@ body, or an external host element addressed by `options.fixedToolbarId`). Impera
212
212
  border-radius: var(--_rti--border-radius);
213
213
  color: var(--_rti--color);
214
214
  cursor: text;
215
- box-shadow: inset 0 -0.13em var(--_rti--underline-color);
215
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_rti--underline-color);
216
216
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
217
217
  }
218
218
  .rich-text-input--sm {
@@ -40,7 +40,8 @@
40
40
  font-size: var(--_sel--font-size);
41
41
  line-height: var(--sc-kit--leading--tight);
42
42
  cursor: pointer;
43
- box-shadow: inset 0 -0.13em var(--_sel--underline-color);
43
+ // prevents hairline anti-aliasing
44
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_sel--underline-color);
44
45
  @include transitions.transition(background-color border-color color box-shadow);
45
46
 
46
47
  &--sm {
@@ -213,7 +213,7 @@ background--hover}`.
213
213
  font-size: var(--_sel--font-size);
214
214
  line-height: var(--sc-kit--leading--tight);
215
215
  cursor: pointer;
216
- box-shadow: inset 0 -0.13em var(--_sel--underline-color);
216
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_sel--underline-color);
217
217
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
218
218
  }
219
219
  .singleselect--sm {
@@ -428,7 +428,7 @@ padding-inline, font-size, gap, max-width, remove-color, remove-color--hover}`.
428
428
  font-size: var(--_sel--font-size);
429
429
  line-height: var(--sc-kit--leading--tight);
430
430
  cursor: pointer;
431
- box-shadow: inset 0 -0.13em var(--_sel--underline-color);
431
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_sel--underline-color);
432
432
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
433
433
  }
434
434
  .multiselect-trigger--sm {
@@ -186,7 +186,7 @@ exposed via `bind:this`.
186
186
  font-size: var(--_ta--font-size);
187
187
  line-height: var(--sc-kit--leading--normal);
188
188
  cursor: text;
189
- box-shadow: inset 0 -0.13em var(--_ta--underline-color);
189
+ box-shadow: inset 0 calc(-1 * max(2px, 0.125em)) var(--_ta--underline-color);
190
190
  transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default), box-shadow var(--sc-kit--duration--base) var(--sc-kit--ease--default);
191
191
  }
192
192
  .textarea--sm {
@@ -1,8 +1,8 @@
1
- <script lang="ts">let { error, children } = $props();
1
+ <script lang="ts">let { error, reserveErrorSpace = true, children } = $props();
2
2
  export {};
3
3
  </script>
4
4
 
5
- <div class="field-error" class:field-error--errored={!!error}>
5
+ <div class="field-error" class:field-error--errored={!!error} class:field-error--no-reserve={!reserveErrorSpace}>
6
6
  {@render children()}
7
7
  {#if error}
8
8
  <small class="field-error__message">{error}</small>
@@ -12,9 +12,10 @@ export {};
12
12
  <!--
13
13
  @component
14
14
  Internal helper — wraps a form field and renders its error message in an absolutely-positioned slot
15
- below, with reserved space so the layout doesn't shift when the error toggles. Used internally by
16
- `Validatable` / `ValidationError` (and any other field-shaped kit consumer that needs the same
17
- visual contract).
15
+ below, with reserved space so the layout doesn't shift when the error toggles. With
16
+ `reserveErrorSpace={false}` the reserve is dropped the message stays absolutely positioned and
17
+ overlays whatever sits below the field. Used internally by `Validatable` / `ValidationError` (and
18
+ any other field-shaped kit consumer that needs the same visual contract).
18
19
 
19
20
  Not exported from `index.ts` — for kit-internal use only.
20
21
  -->
@@ -24,6 +25,9 @@ Not exported from `index.ts` — for kit-internal use only.
24
25
  margin-bottom: 1.1em;
25
26
  display: block;
26
27
  }
28
+ .field-error--no-reserve {
29
+ margin-bottom: 0;
30
+ }
27
31
  .field-error__message {
28
32
  position: absolute;
29
33
  top: calc(100% + 0.1em);
@@ -2,13 +2,16 @@ import type { Snippet } from 'svelte';
2
2
  type Props = {
3
3
  /** Error message to display below the field. Falsy hides the message and the `--errored` flag. */
4
4
  error?: string | null | undefined;
5
+ /** Reserve space below the field for the error message. Off drops the reserve — the message overlays content below. @default true */
6
+ reserveErrorSpace?: boolean;
5
7
  children: Snippet;
6
8
  };
7
9
  /**
8
10
  * Internal helper — wraps a form field and renders its error message in an absolutely-positioned slot
9
- * below, with reserved space so the layout doesn't shift when the error toggles. Used internally by
10
- * `Validatable` / `ValidationError` (and any other field-shaped kit consumer that needs the same
11
- * visual contract).
11
+ * below, with reserved space so the layout doesn't shift when the error toggles. With
12
+ * `reserveErrorSpace={false}` the reserve is dropped the message stays absolutely positioned and
13
+ * overlays whatever sits below the field. Used internally by `Validatable` / `ValidationError` (and
14
+ * any other field-shaped kit consumer that needs the same visual contract).
12
15
  *
13
16
  * Not exported from `index.ts` — for kit-internal use only.
14
17
  */
@@ -3,7 +3,7 @@
3
3
 
4
4
  <script lang="ts" generics="T extends Record<string, unknown>, K extends keyof T & string">import { Utils } from '../../core/utils';
5
5
  import { default as FieldError } from './cmp.field-error.svelte';
6
- let { handler, name, validateOn = ['input', 'change', 'blur'], debounceMs = 0, disableTouchedTracking = false, on, children } = $props();
6
+ let { handler, name, validateOn = ['input', 'change', 'blur'], debounceMs = 0, disableTouchedTracking = false, reserveErrorSpace = true, on, children } = $props();
7
7
  // True between a value change and the next completed validation. Hides the stale error in the
8
8
  // UI without mutating handler.errors — the model keeps the last known result, the view just
9
9
  // doesn't trust it until the validator has caught up.
@@ -59,7 +59,7 @@ const field = $derived({
59
59
  });
60
60
  </script>
61
61
 
62
- <FieldError error={showErrors ? handler.errors[name] : null}>
62
+ <FieldError error={showErrors ? handler.errors[name] : null} reserveErrorSpace={reserveErrorSpace}>
63
63
  {@render children(field)}
64
64
  </FieldError>
65
65
 
@@ -68,7 +68,7 @@ const field = $derived({
68
68
  Binds a form field to any kit input via a render-prop snippet. The base inputs stay unchanged;
69
69
  `Validatable` is the only place that knows about `FormValidationHandler`. Wraps children in an
70
70
  internal `FieldError` frame that reserves space for the error message so toggling doesn't shift
71
- layout.
71
+ layout; `reserveErrorSpace={false}` drops the reserve — the message overlays content below.
72
72
 
73
73
  ### Usage
74
74
  ```svelte
@@ -30,6 +30,12 @@ declare function $$render<T extends Record<string, unknown>, K extends keyof T &
30
30
  debounceMs?: number;
31
31
  /** Show errors even when the field has not been touched. @default false */
32
32
  disableTouchedTracking?: boolean;
33
+ /**
34
+ * Reserve space below the field for the error message. Turn off when a sibling must align to
35
+ * the field's real bottom edge — the message then overlays whatever sits below the field.
36
+ * @default true
37
+ */
38
+ reserveErrorSpace?: boolean;
33
39
  /**
34
40
  * Extra consumer callbacks. Run AFTER the validation wiring; they cannot override it.
35
41
  * The `children` snippet always receives fully-wired `field.on.*`.
@@ -65,7 +71,7 @@ interface $$IsomorphicComponent {
65
71
  * Binds a form field to any kit input via a render-prop snippet. The base inputs stay unchanged;
66
72
  * `Validatable` is the only place that knows about `FormValidationHandler`. Wraps children in an
67
73
  * internal `FieldError` frame that reserves space for the error message so toggling doesn't shift
68
- * layout.
74
+ * layout; `reserveErrorSpace={false}` drops the reserve — the message overlays content below.
69
75
  *
70
76
  * ### Usage
71
77
  * ```svelte
@@ -1,9 +1,9 @@
1
1
  <script lang="ts" generics="T extends Record<string, unknown>">import { default as FieldError } from './cmp.field-error.svelte';
2
- let { name, validationResult, children } = $props();
2
+ let { name, validationResult, reserveErrorSpace = true, children } = $props();
3
3
  const errorText = $derived(validationResult?.errors[name] ?? null);
4
4
  </script>
5
5
 
6
- <FieldError error={errorText}>
6
+ <FieldError error={errorText} reserveErrorSpace={reserveErrorSpace}>
7
7
  {@render children()}
8
8
  </FieldError>
9
9
 
@@ -11,5 +11,6 @@ const errorText = $derived(validationResult?.errors[name] ?? null);
11
11
  @component
12
12
  Wraps a form field and displays validation errors from a `FormValidateResult` object. Stateless
13
13
  sibling of `Validatable` for cases where the consumer already has the validation result in hand
14
- (e.g. server-validated forms) and does not need a `FormValidationHandler`.
14
+ (e.g. server-validated forms) and does not need a `FormValidationHandler`. Pass
15
+ `reserveErrorSpace={false}` to overlay the message instead of reserving layout space.
15
16
  -->
@@ -6,6 +6,12 @@ declare function $$render<T extends Record<string, unknown>>(): {
6
6
  name: keyof T & string;
7
7
  /** Validation result object containing error messages */
8
8
  validationResult?: FormValidateResult<T>;
9
+ /**
10
+ * Reserve space below the field for the error message. Turn off when a sibling must align to
11
+ * the field's real bottom edge — the message then overlays whatever sits below the field.
12
+ * @default true
13
+ */
14
+ reserveErrorSpace?: boolean;
9
15
  children: Snippet;
10
16
  };
11
17
  exports: {};
@@ -30,7 +36,8 @@ interface $$IsomorphicComponent {
30
36
  /**
31
37
  * Wraps a form field and displays validation errors from a `FormValidateResult` object. Stateless
32
38
  * sibling of `Validatable` for cases where the consumer already has the validation result in hand
33
- * (e.g. server-validated forms) and does not need a `FormValidationHandler`.
39
+ * (e.g. server-validated forms) and does not need a `FormValidationHandler`. Pass
40
+ * `reserveErrorSpace={false}` to overlay the message instead of reserving layout space.
34
41
  */
35
42
  declare const Cmp: $$IsomorphicComponent;
36
43
  type Cmp<T extends Record<string, unknown>> = InstanceType<typeof Cmp<T>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",