@mystravil/hardpoint 0.2.0 → 0.3.0

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 (54) hide show
  1. package/CHANGELOG.md +51 -38
  2. package/LICENSE +25 -25
  3. package/README.md +67 -64
  4. package/dist/base.css +625 -625
  5. package/dist/components/AppShell.svelte +698 -698
  6. package/dist/components/Button.svelte +221 -174
  7. package/dist/components/Button.svelte.d.ts +18 -2
  8. package/dist/components/Checkbox.svelte +235 -235
  9. package/dist/components/CheckboxGroup.svelte +207 -207
  10. package/dist/components/Combobox.svelte +901 -901
  11. package/dist/components/Dialog.svelte +359 -359
  12. package/dist/components/Disclosure.svelte +201 -201
  13. package/dist/components/EmptyState.svelte +88 -88
  14. package/dist/components/ErrorState.svelte +135 -135
  15. package/dist/components/ImageViewer.svelte +764 -764
  16. package/dist/components/KeyValueList.svelte +114 -114
  17. package/dist/components/ListDetail.svelte +218 -218
  18. package/dist/components/LoadRegion.svelte +219 -219
  19. package/dist/components/MediaFrame.svelte +110 -110
  20. package/dist/components/MediaImage.svelte +256 -256
  21. package/dist/components/Menu.svelte +275 -258
  22. package/dist/components/MenuCheckboxItem.svelte +119 -119
  23. package/dist/components/MenuGroup.svelte +50 -50
  24. package/dist/components/MenuItem.svelte +94 -94
  25. package/dist/components/MenuRadioGroup.svelte +62 -62
  26. package/dist/components/MenuRadioItem.svelte +110 -110
  27. package/dist/components/MenuSeparator.svelte +28 -28
  28. package/dist/components/Meter.svelte +283 -283
  29. package/dist/components/NativeSelect.svelte +135 -135
  30. package/dist/components/Notice.svelte +262 -262
  31. package/dist/components/Popover.svelte +222 -222
  32. package/dist/components/Progress.svelte +157 -157
  33. package/dist/components/RadioGroup.svelte +440 -440
  34. package/dist/components/Select.svelte +471 -471
  35. package/dist/components/Skeleton.svelte +149 -149
  36. package/dist/components/Spinner.svelte +118 -118
  37. package/dist/components/Stat.svelte +253 -253
  38. package/dist/components/Steps.svelte +189 -189
  39. package/dist/components/Switch.svelte +222 -222
  40. package/dist/components/Table.svelte +504 -504
  41. package/dist/components/Tabs.svelte +246 -246
  42. package/dist/components/Tag.svelte +40 -40
  43. package/dist/components/TextArea.svelte +272 -272
  44. package/dist/components/TextField.svelte +396 -256
  45. package/dist/components/TextField.svelte.d.ts +16 -1
  46. package/dist/components/Timeline.svelte +156 -156
  47. package/dist/components/Toaster.svelte +411 -411
  48. package/dist/components/Tooltip.svelte +200 -200
  49. package/dist/components/TooltipGroup.svelte +46 -46
  50. package/dist/components/internal/Scrim.svelte +52 -52
  51. package/dist/components/internal/Sheet.svelte +263 -263
  52. package/dist/fonts.css +71 -71
  53. package/dist/tokens.css +283 -283
  54. package/package.json +81 -76
@@ -1,256 +1,396 @@
1
- <script lang="ts" module>
2
- import type { HTMLInputAttributes } from 'svelte/elements';
3
-
4
- /** The text input types the well suits. For numbers use `text` with `inputmode="numeric"`. */
5
- export type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'tel' | 'url';
6
-
7
- /** From which width the label sits beside the well: 600px, 900px or 1200px. */
8
- export type TextFieldBreakpoint = 'medium' | 'expanded' | 'large';
9
-
10
- /** Attributes and handlers not listed here go to the `<input>`. */
11
- export type TextFieldProps = Omit<HTMLInputAttributes, 'type' | 'value' | 'children'> & {
12
- /** The caps field label above the well. Required: a placeholder is never the only label. */
13
- label: string;
14
- /** The text. Bindable. */
15
- value?: string;
16
- type?: TextFieldType;
17
- /** Guidance under the well in `--muted`, linked by `aria-describedby`. */
18
- hint?: string;
19
- /**
20
- * The error to show under the well, with an icon, linked by
21
- * `aria-describedby`; it sets `aria-invalid`. Say how to fix the
22
- * problem. The app validates; the field shows what it is given.
23
- */
24
- error?: string;
25
- /**
26
- * The label sits above the well by default. `true` puts it beside the
27
- * well at every width; a breakpoint puts it beside from that width up.
28
- * The hint and the error stay under the well.
29
- */
30
- labelBeside?: boolean | TextFieldBreakpoint;
31
- /** The `<input>`, for example to focus the first error on submit. */
32
- ref?: HTMLInputElement | null;
33
- };
34
- </script>
35
-
36
- <script lang="ts">
37
- import { untrack } from 'svelte';
38
- import { Label } from 'bits-ui';
39
- import { fadeIn } from './internal/fade-in.js';
40
-
41
- const uid = $props.id();
42
-
43
- let {
44
- label,
45
- value = $bindable(''),
46
- type = 'text',
47
- hint,
48
- error,
49
- labelBeside = false,
50
- id = uid,
51
- disabled,
52
- ref = $bindable(null),
53
- class: className,
54
- 'aria-describedby': describedBy,
55
- 'aria-invalid': ariaInvalid,
56
- ...rest
57
- }: TextFieldProps = $props();
58
-
59
- const hintId = $derived(`${id}-hint`);
60
- const errorId = $derived(`${id}-error`);
61
- // The error first, so a screen reader reads it before the hint.
62
- const describedByIds = $derived(
63
- [error ? errorId : undefined, hint ? hintId : undefined, describedBy].filter(Boolean).join(' ') ||
64
- undefined
65
- );
66
-
67
- // ---- Short motion: the error (BRIEF, --dur-message) --------------------
68
-
69
- // An error that appears after the first render fades in from 0 over
70
- // --dur-message, and the well's edge fades to --danger with it (the CSS
71
- // below). An error that clears goes at once, and so does the red edge. A
72
- // field that renders or mounts with an error, such as one in a dialog
73
- // that opens, shows it at once. 0ms is a snap.
74
- let root: HTMLDivElement | null = $state(null);
75
- let errorShown = untrack(() => !!error);
76
- $effect(() => {
77
- const shown = !!error;
78
- untrack(() => {
79
- const appeared = shown && !errorShown;
80
- errorShown = shown;
81
- const message = appeared ? root?.querySelector(':scope > .error') : null;
82
- if (message) fadeIn(message, '--dur-message');
83
- });
84
- });
85
- </script>
86
-
87
- <!--
88
- A single-line text input in the black well, with a plain caps label above
89
- it (Bits UI Label), an optional hint and an inline error below. Extra
90
- attributes (`name`, `inputmode`, `autocomplete`, `enterkeyhint`,
91
- `required`), handlers and `class` go to the <input>.
92
- -->
93
- <div
94
- bind:this={root}
95
- class="hp-text-field"
96
- data-invalid={error ? '' : undefined}
97
- data-disabled={disabled ? '' : undefined}
98
- data-label-beside={labelBeside === true ? 'always' : labelBeside || undefined}
99
- >
100
- <Label.Root for={id}>
101
- {#snippet child({ props })}
102
- <label {...props} class="hp-label">{label}</label>
103
- {/snippet}
104
- </Label.Root>
105
- <input
106
- {...rest}
107
- bind:this={ref}
108
- bind:value
109
- {id}
110
- {type}
111
- {disabled}
112
- class={['hp-well', className]}
113
- aria-describedby={describedByIds}
114
- aria-invalid={error ? true : ariaInvalid}
115
- />
116
- {#if hint}
117
- <p class="hint hp-caption" id={hintId}>{hint}</p>
118
- {/if}
119
- {#if error}
120
- <p class="error hp-caption" id={errorId}>
121
- <span class="icon" aria-hidden="true">
122
- <svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor">
123
- <path d="M1.5 1.5h13v13h-13z" />
124
- <path d="M8 4v4M8 10v2" stroke-width="2" />
125
- </svg>
126
- </span>
127
- <span>{error}</span>
128
- </p>
129
- {/if}
130
- </div>
131
-
132
- <style>
133
- .hp-text-field {
134
- display: grid;
135
- gap: var(--space-1);
136
- min-width: 0;
137
- }
138
- label {
139
- color: var(--muted);
140
- }
141
- input {
142
- display: block;
143
- width: 100%;
144
- min-width: 0;
145
- }
146
- input::placeholder {
147
- color: var(--muted);
148
- opacity: 1;
149
- }
150
-
151
- /* Invalid: the boundary turns --danger (5.9:1 on black), never alone:
152
- the icon and the message say what is wrong. Keyed on the root's
153
- data-invalid, set by `error`, so an aria-invalid the app passes
154
- without a message draws no red border. The edge fades to --danger
155
- over --dur-message with the message; the transition belongs to this
156
- rule, so the edge goes back at once when the error clears. The hover
157
- fill keeps base.css's timing. */
158
- [data-invalid] input {
159
- border-color: var(--danger);
160
- transition:
161
- background-color var(--dur-hover) var(--settle),
162
- border-color var(--dur-message) var(--settle);
163
- }
164
- [data-invalid] input:disabled {
165
- border-color: var(--border);
166
- }
167
-
168
- .hint {
169
- margin: 0;
170
- color: var(--muted);
171
- }
172
- .error {
173
- display: flex;
174
- gap: var(--space-2);
175
- margin: 0;
176
- color: var(--danger);
177
- }
178
- /* The first message starts 8px under the well, clear of the focus
179
- contour (2px, drawn 4px outside the well). */
180
- input + .hint,
181
- input + .error {
182
- margin-top: var(--space-1);
183
- }
184
- /* One line box high, so the icon centres on the first line of the
185
- message however it wraps. */
186
- .icon {
187
- display: flex;
188
- flex: none;
189
- align-items: center;
190
- height: 1lh;
191
- }
192
- .icon svg {
193
- display: block;
194
- }
195
-
196
- /* Beside the well, the label keeps its one line in the first column; the
197
- well, hint and error share the second. */
198
- [data-label-beside='always'] {
199
- grid-template-columns: auto minmax(0, 1fr);
200
- column-gap: var(--space-2);
201
- }
202
- [data-label-beside='always'] label {
203
- grid-row: 1;
204
- align-self: center;
205
- white-space: nowrap;
206
- }
207
- [data-label-beside='always'] input,
208
- [data-label-beside='always'] p {
209
- grid-column: 2;
210
- }
211
- @media (min-width: 600px) {
212
- [data-label-beside='medium'] {
213
- grid-template-columns: auto minmax(0, 1fr);
214
- column-gap: var(--space-2);
215
- }
216
- [data-label-beside='medium'] label {
217
- grid-row: 1;
218
- align-self: center;
219
- white-space: nowrap;
220
- }
221
- [data-label-beside='medium'] input,
222
- [data-label-beside='medium'] p {
223
- grid-column: 2;
224
- }
225
- }
226
- @media (min-width: 900px) {
227
- [data-label-beside='expanded'] {
228
- grid-template-columns: auto minmax(0, 1fr);
229
- column-gap: var(--space-2);
230
- }
231
- [data-label-beside='expanded'] label {
232
- grid-row: 1;
233
- align-self: center;
234
- white-space: nowrap;
235
- }
236
- [data-label-beside='expanded'] input,
237
- [data-label-beside='expanded'] p {
238
- grid-column: 2;
239
- }
240
- }
241
- @media (min-width: 1200px) {
242
- [data-label-beside='large'] {
243
- grid-template-columns: auto minmax(0, 1fr);
244
- column-gap: var(--space-2);
245
- }
246
- [data-label-beside='large'] label {
247
- grid-row: 1;
248
- align-self: center;
249
- white-space: nowrap;
250
- }
251
- [data-label-beside='large'] input,
252
- [data-label-beside='large'] p {
253
- grid-column: 2;
254
- }
255
- }
256
- </style>
1
+ <script lang="ts" module>
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+
4
+ /** The text input types the well suits. For numbers use `text` with `inputmode="numeric"`. */
5
+ export type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'tel' | 'url';
6
+
7
+ /** From which width the label sits beside the well: 600px, 900px or 1200px. */
8
+ export type TextFieldBreakpoint = 'medium' | 'expanded' | 'large';
9
+
10
+ /** Attributes and handlers not listed here go to the `<input>`. */
11
+ export type TextFieldProps = Omit<HTMLInputAttributes, 'type' | 'value' | 'children'> & {
12
+ /**
13
+ * The caps field label above the well. Required, also with
14
+ * `labelHidden`: a placeholder is never the only label.
15
+ */
16
+ label: string;
17
+ /** The text. Bindable. */
18
+ value?: string;
19
+ type?: TextFieldType;
20
+ /** Guidance under the well in `--muted`, linked by `aria-describedby`. */
21
+ hint?: string;
22
+ /**
23
+ * The error to show under the well, with an icon, linked by
24
+ * `aria-describedby`; it sets `aria-invalid`. Say how to fix the
25
+ * problem. The app validates; the field shows what it is given.
26
+ */
27
+ error?: string;
28
+ /**
29
+ * The label sits above the well by default. `true` puts it beside the
30
+ * well at every width; a breakpoint puts it beside from that width up.
31
+ * The hint and the error stay under the well.
32
+ */
33
+ labelBeside?: boolean | TextFieldBreakpoint;
34
+ /**
35
+ * Hides the label from sight; it stays the field's accessible name.
36
+ * For a field whose purpose its place makes plain, such as a search in
37
+ * a toolbar. The well keeps its look and its 16 px text. It overrides
38
+ * `labelBeside`.
39
+ */
40
+ labelHidden?: boolean;
41
+ /**
42
+ * The accessible name of the clear button that `type="search"` shows
43
+ * inside the well while it holds text.
44
+ */
45
+ clearLabel?: string;
46
+ /** The `<input>`, for example to focus the first error on submit. */
47
+ ref?: HTMLInputElement | null;
48
+ };
49
+ </script>
50
+
51
+ <script lang="ts">
52
+ import { untrack } from 'svelte';
53
+ import { Label } from 'bits-ui';
54
+ import { fadeIn } from './internal/fade-in.js';
55
+
56
+ const uid = $props.id();
57
+
58
+ let {
59
+ label,
60
+ value = $bindable(''),
61
+ type = 'text',
62
+ hint,
63
+ error,
64
+ labelBeside = false,
65
+ labelHidden = false,
66
+ clearLabel = 'Clear',
67
+ id = uid,
68
+ disabled,
69
+ readonly,
70
+ ref = $bindable(null),
71
+ class: className,
72
+ 'aria-describedby': describedBy,
73
+ 'aria-invalid': ariaInvalid,
74
+ ...rest
75
+ }: TextFieldProps = $props();
76
+
77
+ const hintId = $derived(`${id}-hint`);
78
+ const errorId = $derived(`${id}-error`);
79
+ // The error first, so a screen reader reads it before the hint.
80
+ const describedByIds = $derived(
81
+ [error ? errorId : undefined, hint ? hintId : undefined, describedBy].filter(Boolean).join(' ') ||
82
+ undefined
83
+ );
84
+
85
+ // ---- Short motion: the error (BRIEF, --dur-message) --------------------
86
+
87
+ // An error that appears after the first render fades in from 0 over
88
+ // --dur-message, and the well's edge fades to --danger with it (the CSS
89
+ // below). An error that clears goes at once, and so does the red edge. A
90
+ // field that renders or mounts with an error, such as one in a dialog
91
+ // that opens, shows it at once. 0ms is a snap.
92
+ let root: HTMLDivElement | null = $state(null);
93
+ let errorShown = untrack(() => !!error);
94
+ $effect(() => {
95
+ const shown = !!error;
96
+ untrack(() => {
97
+ const appeared = shown && !errorShown;
98
+ errorShown = shown;
99
+ const message = appeared ? root?.querySelector(':scope > .error') : null;
100
+ if (message) fadeIn(message, '--dur-message');
101
+ });
102
+ });
103
+
104
+ // ---- Search: the clear button --------------------------------------
105
+
106
+ // The browser's own cancel button is hidden (CSS below) and this one
107
+ // takes its place, so every engine draws the same control. It writes
108
+ // the empty value and fires a bubbling `input`, as typing would, so
109
+ // `bind:value` and the app's `oninput` both hear it, then focus returns
110
+ // to the field. A mouse or a touch press never takes focus from the
111
+ // field, so an on-screen keyboard stays up; keys reach the button by Tab.
112
+ const clearable = $derived(type === 'search');
113
+ const showClear = $derived(clearable && !!value && !disabled && !readonly);
114
+
115
+ function clear() {
116
+ if (!ref) return;
117
+ ref.value = '';
118
+ ref.dispatchEvent(new Event('input', { bubbles: true }));
119
+ ref.focus();
120
+ }
121
+ </script>
122
+
123
+ <!--
124
+ A single-line text input in the black well, with a plain caps label above
125
+ it (Bits UI Label), an optional hint and an inline error below. Extra
126
+ attributes (`name`, `inputmode`, `autocomplete`, `enterkeyhint`,
127
+ `required`), handlers and `class` go to the <input>. With
128
+ `type="search"` the well holds a clear button at its end while it has
129
+ text.
130
+ -->
131
+ <div
132
+ bind:this={root}
133
+ class="hp-text-field"
134
+ data-invalid={error ? '' : undefined}
135
+ data-disabled={disabled ? '' : undefined}
136
+ data-label-beside={labelHidden ? undefined : labelBeside === true ? 'always' : labelBeside || undefined}
137
+ >
138
+ <Label.Root for={id}>
139
+ {#snippet child({ props })}
140
+ <label {...props} class={['hp-label', labelHidden && 'hp-visually-hidden']}>{label}</label>
141
+ {/snippet}
142
+ </Label.Root>
143
+ {#snippet well()}
144
+ <input
145
+ {...rest}
146
+ bind:this={ref}
147
+ bind:value
148
+ {id}
149
+ {type}
150
+ {disabled}
151
+ {readonly}
152
+ class={['hp-well', className]}
153
+ aria-describedby={describedByIds}
154
+ aria-invalid={error ? true : ariaInvalid}
155
+ />
156
+ {/snippet}
157
+ {#if clearable}
158
+ <div class="search">
159
+ {@render well()}
160
+ {#if showClear}
161
+ <button
162
+ type="button"
163
+ class="clear hp-focus-inset"
164
+ aria-label={clearLabel}
165
+ onmousedown={(e) => e.preventDefault()}
166
+ onclick={clear}
167
+ >
168
+ <svg
169
+ viewBox="0 0 16 16"
170
+ fill="none"
171
+ stroke="currentColor"
172
+ stroke-width="1.5"
173
+ stroke-linecap="square"
174
+ aria-hidden="true"
175
+ >
176
+ <path d="M4 4l8 8M12 4l-8 8" />
177
+ </svg>
178
+ </button>
179
+ {/if}
180
+ </div>
181
+ {:else}
182
+ {@render well()}
183
+ {/if}
184
+ {#if hint}
185
+ <p class="hint hp-caption" id={hintId}>{hint}</p>
186
+ {/if}
187
+ {#if error}
188
+ <p class="error hp-caption" id={errorId}>
189
+ <span class="icon" aria-hidden="true">
190
+ <svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor">
191
+ <path d="M1.5 1.5h13v13h-13z" />
192
+ <path d="M8 4v4M8 10v2" stroke-width="2" />
193
+ </svg>
194
+ </span>
195
+ <span>{error}</span>
196
+ </p>
197
+ {/if}
198
+ </div>
199
+
200
+ <style>
201
+ .hp-text-field {
202
+ display: grid;
203
+ gap: var(--space-1);
204
+ min-width: 0;
205
+ }
206
+ label {
207
+ color: var(--muted);
208
+ }
209
+ input {
210
+ display: block;
211
+ width: 100%;
212
+ min-width: 0;
213
+ }
214
+ input::placeholder {
215
+ color: var(--muted);
216
+ opacity: 1;
217
+ }
218
+
219
+ /* ---- Search: the clear button replaces the browser's own --------- */
220
+
221
+ /* WebKit and Blink draw a cancel button in a search input whatever its
222
+ appearance; Chrome on Android draws it bright blue. */
223
+ input::-webkit-search-cancel-button,
224
+ input::-webkit-search-decoration {
225
+ appearance: none;
226
+ display: none;
227
+ }
228
+ .search {
229
+ position: relative;
230
+ min-width: 0;
231
+ }
232
+ /* The text stops short of the button's square, whether it shows or not,
233
+ so the text never moves when it appears. */
234
+ .search > input {
235
+ padding-right: var(--control-height);
236
+ }
237
+ /* A --control-height square over the end of the well: 36px on fine
238
+ pointers, 44px on coarse. Its transparent border keeps a press fill
239
+ inside the well's edge. The cross is smaller than a close button's,
240
+ in --muted like the placeholder, so it reads as part of the well. */
241
+ .clear {
242
+ position: absolute;
243
+ top: 0;
244
+ right: 0;
245
+ display: grid;
246
+ place-items: center;
247
+ box-sizing: border-box;
248
+ width: var(--control-height);
249
+ height: var(--control-height);
250
+ padding: 0;
251
+ border: 1px solid transparent;
252
+ background: transparent;
253
+ background-clip: padding-box;
254
+ color: var(--muted);
255
+ cursor: pointer;
256
+ -webkit-tap-highlight-color: transparent;
257
+ }
258
+ .clear svg {
259
+ display: block;
260
+ width: var(--space-4);
261
+ height: var(--space-4);
262
+ }
263
+ .clear:active {
264
+ background-color: var(--surface-raised);
265
+ color: var(--text);
266
+ }
267
+ @media (hover: hover) and (pointer: fine) {
268
+ .clear {
269
+ transition:
270
+ background-color var(--dur-hover) var(--settle),
271
+ color var(--dur-hover) var(--settle);
272
+ }
273
+ .clear:hover {
274
+ color: var(--text);
275
+ }
276
+ .clear:active {
277
+ transition-duration: var(--dur-press);
278
+ }
279
+ /* The button is part of the well, so the well keeps its hover fill. */
280
+ .search:has(> .clear:hover) > input {
281
+ background-color: var(--surface-hover);
282
+ }
283
+ }
284
+
285
+ /* Invalid: the boundary turns --danger (5.9:1 on black), never alone:
286
+ the icon and the message say what is wrong. Keyed on the root's
287
+ data-invalid, set by `error`, so an aria-invalid the app passes
288
+ without a message draws no red border. The edge fades to --danger
289
+ over --dur-message with the message; the transition belongs to this
290
+ rule, so the edge goes back at once when the error clears. The hover
291
+ fill keeps base.css's timing. */
292
+ [data-invalid] input {
293
+ border-color: var(--danger);
294
+ transition:
295
+ background-color var(--dur-hover) var(--settle),
296
+ border-color var(--dur-message) var(--settle);
297
+ }
298
+ [data-invalid] input:disabled {
299
+ border-color: var(--border);
300
+ }
301
+
302
+ .hint {
303
+ margin: 0;
304
+ color: var(--muted);
305
+ }
306
+ .error {
307
+ display: flex;
308
+ gap: var(--space-2);
309
+ margin: 0;
310
+ color: var(--danger);
311
+ }
312
+ /* The first message starts 8px under the well, clear of the focus
313
+ contour (2px, drawn 4px outside the well). */
314
+ input + .hint,
315
+ input + .error,
316
+ .search + .hint,
317
+ .search + .error {
318
+ margin-top: var(--space-1);
319
+ }
320
+ /* One line box high, so the icon centres on the first line of the
321
+ message however it wraps. */
322
+ .icon {
323
+ display: flex;
324
+ flex: none;
325
+ align-items: center;
326
+ height: 1lh;
327
+ }
328
+ .icon svg {
329
+ display: block;
330
+ }
331
+
332
+ /* Beside the well, the label keeps its one line in the first column; the
333
+ well, hint and error share the second. */
334
+ [data-label-beside='always'] {
335
+ grid-template-columns: auto minmax(0, 1fr);
336
+ column-gap: var(--space-2);
337
+ }
338
+ [data-label-beside='always'] label {
339
+ grid-row: 1;
340
+ align-self: center;
341
+ white-space: nowrap;
342
+ }
343
+ [data-label-beside='always'] input,
344
+ [data-label-beside='always'] .search,
345
+ [data-label-beside='always'] p {
346
+ grid-column: 2;
347
+ }
348
+ @media (min-width: 600px) {
349
+ [data-label-beside='medium'] {
350
+ grid-template-columns: auto minmax(0, 1fr);
351
+ column-gap: var(--space-2);
352
+ }
353
+ [data-label-beside='medium'] label {
354
+ grid-row: 1;
355
+ align-self: center;
356
+ white-space: nowrap;
357
+ }
358
+ [data-label-beside='medium'] input,
359
+ [data-label-beside='medium'] .search,
360
+ [data-label-beside='medium'] p {
361
+ grid-column: 2;
362
+ }
363
+ }
364
+ @media (min-width: 900px) {
365
+ [data-label-beside='expanded'] {
366
+ grid-template-columns: auto minmax(0, 1fr);
367
+ column-gap: var(--space-2);
368
+ }
369
+ [data-label-beside='expanded'] label {
370
+ grid-row: 1;
371
+ align-self: center;
372
+ white-space: nowrap;
373
+ }
374
+ [data-label-beside='expanded'] input,
375
+ [data-label-beside='expanded'] .search,
376
+ [data-label-beside='expanded'] p {
377
+ grid-column: 2;
378
+ }
379
+ }
380
+ @media (min-width: 1200px) {
381
+ [data-label-beside='large'] {
382
+ grid-template-columns: auto minmax(0, 1fr);
383
+ column-gap: var(--space-2);
384
+ }
385
+ [data-label-beside='large'] label {
386
+ grid-row: 1;
387
+ align-self: center;
388
+ white-space: nowrap;
389
+ }
390
+ [data-label-beside='large'] input,
391
+ [data-label-beside='large'] .search,
392
+ [data-label-beside='large'] p {
393
+ grid-column: 2;
394
+ }
395
+ }
396
+ </style>