@dryui/ui 1.7.3 → 1.8.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.
- package/dist/button/button.svelte +31 -2
- package/dist/button/button.svelte.d.ts +3 -1
- package/dist/button/index.d.ts +3 -1
- package/dist/chip-group/chip-group-label.svelte +26 -0
- package/dist/chip-group/chip-group-label.svelte.d.ts +8 -0
- package/dist/chip-group/chip-group-root.svelte +47 -9
- package/dist/chip-group/chip-group-root.svelte.d.ts +2 -0
- package/dist/chip-group/index.d.ts +4 -1
- package/dist/chip-group/index.js +3 -1
- package/dist/heading/heading.svelte +28 -6
- package/dist/heading/heading.svelte.d.ts +1 -0
- package/dist/heading/index.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/text/index.d.ts +6 -0
- package/dist/text/text.svelte +24 -3
- package/dist/text/text.svelte.d.ts +1 -0
- package/dist/themes/dark.css +9 -1
- package/dist/typography/heading.svelte +2 -1
- package/dist/typography/text.svelte +12 -1
- package/package.json +6 -6
- package/skills/dryui/rules/accessibility.md +2 -6
- package/skills/dryui/rules/compound-components.md +2 -6
- package/skills/dryui/rules/theming.md +20 -0
|
@@ -26,11 +26,15 @@
|
|
|
26
26
|
| 'pill';
|
|
27
27
|
size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg';
|
|
28
28
|
// Autocomplete still suggests the canonical values via the literal union.
|
|
29
|
-
|
|
29
|
+
// 'ink' renders a solid near-black editorial CTA in light theme that auto-inverts
|
|
30
|
+
// (light bg, dark text) in dark theme, using --dry-color-{bg,text}-inverse tokens.
|
|
31
|
+
color?: 'primary' | 'danger' | 'ink' | (string & {}) | null;
|
|
30
32
|
href?: string;
|
|
31
33
|
rel?: string;
|
|
32
34
|
target?: string;
|
|
33
35
|
download?: boolean | string;
|
|
36
|
+
/** Back-compat alias for `class` — matches Heading/Text ergonomics. Prefer `class`. */
|
|
37
|
+
className?: HTMLButtonAttributes['class'];
|
|
34
38
|
/** Callback invoked with the rendered `<button>` or `<a>` element on mount, `null` on destroy. */
|
|
35
39
|
ref?: (el: HTMLButtonElement | HTMLAnchorElement | null) => void;
|
|
36
40
|
children: Snippet;
|
|
@@ -52,7 +56,8 @@
|
|
|
52
56
|
download,
|
|
53
57
|
type = 'button',
|
|
54
58
|
onclick,
|
|
55
|
-
class:
|
|
59
|
+
class: classAttr,
|
|
60
|
+
className = classAttr,
|
|
56
61
|
ref,
|
|
57
62
|
children,
|
|
58
63
|
...rest
|
|
@@ -451,6 +456,30 @@
|
|
|
451
456
|
--_dry-btn-on-accent: var(--dry-btn-on-accent, var(--dry-color-on-error));
|
|
452
457
|
}
|
|
453
458
|
|
|
459
|
+
/* ── Ink: editorial "download / primary CTA" preset.
|
|
460
|
+
Uses the semantic `inverse` tokens so the surface flips between themes:
|
|
461
|
+
light theme → near-black bg + white text; dark theme → white bg + near-black
|
|
462
|
+
text. Any consumer token override (--dry-btn-bg etc.) still wins because the
|
|
463
|
+
variant styles read from the public layer first. */
|
|
464
|
+
:is(a, button)[data-color='ink'] {
|
|
465
|
+
--_dry-btn-accent: var(--dry-btn-accent, var(--dry-color-bg-inverse));
|
|
466
|
+
--_dry-btn-accent-fg: var(--dry-btn-accent-fg, var(--dry-color-text-inverse));
|
|
467
|
+
--_dry-btn-accent-stroke: var(--dry-btn-accent-stroke, var(--dry-color-stroke-strong));
|
|
468
|
+
--_dry-btn-accent-weak: var(
|
|
469
|
+
--dry-btn-accent-weak,
|
|
470
|
+
color-mix(in srgb, var(--dry-color-bg-inverse) 10%, transparent)
|
|
471
|
+
);
|
|
472
|
+
--_dry-btn-accent-hover: var(
|
|
473
|
+
--dry-btn-accent-hover,
|
|
474
|
+
color-mix(in srgb, var(--dry-color-bg-inverse) 85%, transparent)
|
|
475
|
+
);
|
|
476
|
+
--_dry-btn-accent-active: var(
|
|
477
|
+
--dry-btn-accent-active,
|
|
478
|
+
color-mix(in srgb, var(--dry-color-bg-inverse) 75%, transparent)
|
|
479
|
+
);
|
|
480
|
+
--_dry-btn-on-accent: var(--dry-btn-on-accent, var(--dry-color-text-inverse));
|
|
481
|
+
}
|
|
482
|
+
|
|
454
483
|
/* ── Sizes ─────────────────────────────────────────────────────────────────── */
|
|
455
484
|
|
|
456
485
|
:is(a, button)[data-size='sm'] {
|
|
@@ -3,11 +3,13 @@ import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
|
3
3
|
interface Props extends Omit<HTMLButtonAttributes, 'color'> {
|
|
4
4
|
variant?: 'solid' | 'outline' | 'ghost' | 'soft' | 'secondary' | 'link' | 'bare' | 'trigger' | 'nav' | 'tab' | 'toggle' | 'pill';
|
|
5
5
|
size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg';
|
|
6
|
-
color?: 'primary' | 'danger' | (string & {}) | null;
|
|
6
|
+
color?: 'primary' | 'danger' | 'ink' | (string & {}) | null;
|
|
7
7
|
href?: string;
|
|
8
8
|
rel?: string;
|
|
9
9
|
target?: string;
|
|
10
10
|
download?: boolean | string;
|
|
11
|
+
/** Back-compat alias for `class` — matches Heading/Text ergonomics. Prefer `class`. */
|
|
12
|
+
className?: HTMLButtonAttributes['class'];
|
|
11
13
|
/** Callback invoked with the rendered `<button>` or `<a>` element on mount, `null` on destroy. */
|
|
12
14
|
ref?: (el: HTMLButtonElement | HTMLAnchorElement | null) => void;
|
|
13
15
|
children: Snippet;
|
package/dist/button/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ButtonProps as PrimitiveButtonProps } from '@dryui/primitives';
|
|
2
2
|
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft' | 'secondary' | 'link' | 'bare' | 'trigger' | 'nav' | 'tab' | 'toggle' | 'pill';
|
|
3
3
|
export type ButtonSize = 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg';
|
|
4
|
-
export type ButtonColor = 'primary' | 'danger' | (string & {}) | null;
|
|
4
|
+
export type ButtonColor = 'primary' | 'danger' | 'ink' | (string & {}) | null;
|
|
5
5
|
export interface ButtonProps extends Omit<PrimitiveButtonProps, 'color'> {
|
|
6
6
|
variant?: ButtonVariant;
|
|
7
7
|
size?: ButtonSize;
|
|
8
8
|
color?: ButtonColor;
|
|
9
|
+
/** Back-compat alias for `class` — matches Heading/Text ergonomics. Prefer `class`. */
|
|
10
|
+
className?: string;
|
|
9
11
|
}
|
|
10
12
|
export { default as Button } from './button.svelte';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
children: Snippet;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { class: className, children, ...rest }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<span data-chip-group-label class={className} {...rest}>
|
|
13
|
+
{@render children()}
|
|
14
|
+
</span>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
[data-chip-group-label] {
|
|
18
|
+
display: inline-block;
|
|
19
|
+
font-size: var(--dry-chip-group-label-size, var(--dry-type-tiny-size, var(--dry-text-xs-size)));
|
|
20
|
+
font-weight: var(--dry-chip-group-label-weight, 600);
|
|
21
|
+
letter-spacing: var(--dry-chip-group-label-tracking, 0.1em);
|
|
22
|
+
line-height: var(--dry-chip-group-label-leading, 1.2);
|
|
23
|
+
text-transform: uppercase;
|
|
24
|
+
color: var(--dry-chip-group-label-color, var(--dry-color-text-weak));
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
}
|
|
6
|
+
declare const ChipGroupLabel: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ChipGroupLabel = ReturnType<typeof ChipGroupLabel>;
|
|
8
|
+
export default ChipGroupLabel;
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
orientation?: 'horizontal' | 'vertical';
|
|
12
12
|
size?: 'sm' | 'md';
|
|
13
|
+
gap?: 'sm' | 'md' | 'lg';
|
|
14
|
+
justify?: 'start' | 'center' | 'end' | 'between';
|
|
13
15
|
children: Snippet;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -19,6 +21,8 @@
|
|
|
19
21
|
disabled = false,
|
|
20
22
|
orientation = 'horizontal',
|
|
21
23
|
size = 'sm',
|
|
24
|
+
gap = 'md',
|
|
25
|
+
justify = 'start',
|
|
22
26
|
class: className,
|
|
23
27
|
children,
|
|
24
28
|
...rest
|
|
@@ -52,7 +56,7 @@
|
|
|
52
56
|
<div
|
|
53
57
|
role="group"
|
|
54
58
|
data-chip-group
|
|
55
|
-
{...variantAttrs({ orientation, size })}
|
|
59
|
+
{...variantAttrs({ orientation, size, gap, justify })}
|
|
56
60
|
class={className}
|
|
57
61
|
{...rest}
|
|
58
62
|
>
|
|
@@ -60,21 +64,55 @@
|
|
|
60
64
|
</div>
|
|
61
65
|
|
|
62
66
|
<style>
|
|
67
|
+
/*
|
|
68
|
+
* ChipGroup wraps tag/chip clusters with content-driven flow.
|
|
69
|
+
* This is the sanctioned home for flex-wrap: the `[data-chip-group]`
|
|
70
|
+
* attribute is carved out of `dryui/no-flex` so chip/tag wrapping
|
|
71
|
+
* reads naturally without grid hacks.
|
|
72
|
+
*/
|
|
63
73
|
[data-chip-group] {
|
|
64
|
-
display:
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: var(--dry-chip-group-gap, var(--dry-space-2));
|
|
67
78
|
}
|
|
68
79
|
|
|
69
|
-
[data-chip-group][data-orientation='
|
|
70
|
-
|
|
80
|
+
[data-chip-group][data-orientation='vertical'] {
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
align-items: flex-start;
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
/* Gap presets — map onto --dry-space tokens. */
|
|
86
|
+
[data-chip-group][data-gap='sm'] {
|
|
87
|
+
--dry-chip-group-gap: var(--dry-space-1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-chip-group][data-gap='md'] {
|
|
91
|
+
--dry-chip-group-gap: var(--dry-space-2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[data-chip-group][data-gap='lg'] {
|
|
95
|
+
--dry-chip-group-gap: var(--dry-space-3);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Justify presets. */
|
|
99
|
+
[data-chip-group][data-justify='start'] {
|
|
100
|
+
justify-content: flex-start;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-chip-group][data-justify='center'] {
|
|
104
|
+
justify-content: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-chip-group][data-justify='end'] {
|
|
108
|
+
justify-content: flex-end;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[data-chip-group][data-justify='between'] {
|
|
112
|
+
justify-content: space-between;
|
|
76
113
|
}
|
|
77
114
|
|
|
115
|
+
/* Size presets retained for backward-compatible Item styling. */
|
|
78
116
|
[data-chip-group][data-size='sm'] {
|
|
79
117
|
--dry-chip-group-font-size: var(--dry-type-tiny-size, var(--dry-text-xs-size));
|
|
80
118
|
--dry-chip-group-padding-x: var(--dry-space-2_5);
|
|
@@ -6,6 +6,8 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
orientation?: 'horizontal' | 'vertical';
|
|
8
8
|
size?: 'sm' | 'md';
|
|
9
|
+
gap?: 'sm' | 'md' | 'lg';
|
|
10
|
+
justify?: 'start' | 'center' | 'end' | 'between';
|
|
9
11
|
children: Snippet;
|
|
10
12
|
}
|
|
11
13
|
declare const ChipGroupRoot: import("svelte").Component<Props, {}, "value">;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { ChipGroupRootProps as PrimitiveChipGroupRootProps } from '@dryui/primitives';
|
|
1
|
+
import type { ChipGroupRootProps as PrimitiveChipGroupRootProps, ChipGroupLabelProps as PrimitiveChipGroupLabelProps } from '@dryui/primitives';
|
|
2
2
|
export type { ChipGroupItemProps } from '@dryui/primitives';
|
|
3
3
|
export interface ChipGroupRootProps extends PrimitiveChipGroupRootProps {
|
|
4
4
|
size?: 'sm' | 'md';
|
|
5
5
|
}
|
|
6
|
+
export type ChipGroupLabelProps = PrimitiveChipGroupLabelProps;
|
|
6
7
|
import ChipGroupRoot from './chip-group-root.svelte';
|
|
7
8
|
import ChipGroupItem from './chip-group-button-item.svelte';
|
|
9
|
+
import ChipGroupLabel from './chip-group-label.svelte';
|
|
8
10
|
export declare const ChipGroup: {
|
|
9
11
|
Root: typeof ChipGroupRoot;
|
|
10
12
|
Item: typeof ChipGroupItem;
|
|
13
|
+
Label: typeof ChipGroupLabel;
|
|
11
14
|
};
|
package/dist/chip-group/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import ChipGroupRoot from './chip-group-root.svelte';
|
|
2
2
|
import ChipGroupItem from './chip-group-button-item.svelte';
|
|
3
|
+
import ChipGroupLabel from './chip-group-label.svelte';
|
|
3
4
|
export const ChipGroup = {
|
|
4
5
|
Root: ChipGroupRoot,
|
|
5
|
-
Item: ChipGroupItem
|
|
6
|
+
Item: ChipGroupItem,
|
|
7
|
+
Label: ChipGroupLabel
|
|
6
8
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
interface Props extends HTMLAttributes<HTMLHeadingElement> {
|
|
7
7
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
8
8
|
variant?: 'default' | 'display';
|
|
9
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
9
10
|
className?: HTMLAttributes<HTMLHeadingElement>['class'];
|
|
10
11
|
children: Snippet;
|
|
11
12
|
}
|
|
@@ -13,35 +14,40 @@
|
|
|
13
14
|
let {
|
|
14
15
|
level = 2,
|
|
15
16
|
variant = 'default',
|
|
17
|
+
maxMeasure = false,
|
|
16
18
|
class: classAttr,
|
|
17
19
|
className = classAttr,
|
|
18
20
|
children,
|
|
19
21
|
...rest
|
|
20
22
|
}: Props = $props();
|
|
23
|
+
|
|
24
|
+
let measure: 'narrow' | 'default' | 'wide' | undefined = $derived(
|
|
25
|
+
maxMeasure === false ? undefined : maxMeasure
|
|
26
|
+
);
|
|
21
27
|
</script>
|
|
22
28
|
|
|
23
29
|
{#if level === 1}
|
|
24
|
-
<h1 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
30
|
+
<h1 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
25
31
|
{@render children()}
|
|
26
32
|
</h1>
|
|
27
33
|
{:else if level === 2}
|
|
28
|
-
<h2 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
34
|
+
<h2 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
29
35
|
{@render children()}
|
|
30
36
|
</h2>
|
|
31
37
|
{:else if level === 3}
|
|
32
|
-
<h3 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
38
|
+
<h3 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
33
39
|
{@render children()}
|
|
34
40
|
</h3>
|
|
35
41
|
{:else if level === 4}
|
|
36
|
-
<h4 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
42
|
+
<h4 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
37
43
|
{@render children()}
|
|
38
44
|
</h4>
|
|
39
45
|
{:else if level === 5}
|
|
40
|
-
<h5 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
46
|
+
<h5 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
41
47
|
{@render children()}
|
|
42
48
|
</h5>
|
|
43
49
|
{:else}
|
|
44
|
-
<h6 class={className} {...variantAttrs({ level, variant })} {...rest}>
|
|
50
|
+
<h6 class={className} {...variantAttrs({ level, variant, measure })} {...rest}>
|
|
45
51
|
{@render children()}
|
|
46
52
|
</h6>
|
|
47
53
|
{/if}
|
|
@@ -93,8 +99,24 @@
|
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
[data-variant='display'] {
|
|
102
|
+
font-family: var(--dry-font-display, var(--dry-font-sans));
|
|
96
103
|
font-size: var(--dry-type-display-size, var(--dry-text-4xl-size, 2.25rem));
|
|
97
104
|
line-height: var(--dry-type-display-leading, 4rem);
|
|
98
105
|
letter-spacing: -0.04em;
|
|
99
106
|
}
|
|
107
|
+
|
|
108
|
+
/* ── Measure (max-inline-size) ─────────────────────────────────────────────
|
|
109
|
+
ch-unit measure tracks text content, not viewport layout. Editorial
|
|
110
|
+
headlines wrap at narrow widths (~22ch) for rhythm. */
|
|
111
|
+
[data-measure='narrow'] {
|
|
112
|
+
max-inline-size: 22ch;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
[data-measure='default'] {
|
|
116
|
+
max-inline-size: 45ch;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-measure='wide'] {
|
|
120
|
+
max-inline-size: 65ch;
|
|
121
|
+
}
|
|
100
122
|
</style>
|
|
@@ -3,6 +3,7 @@ import type { HTMLAttributes } from 'svelte/elements';
|
|
|
3
3
|
interface Props extends HTMLAttributes<HTMLHeadingElement> {
|
|
4
4
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
5
5
|
variant?: 'default' | 'display';
|
|
6
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
6
7
|
className?: HTMLAttributes<HTMLHeadingElement>['class'];
|
|
7
8
|
children: Snippet;
|
|
8
9
|
}
|
package/dist/heading/index.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ import type { HTMLAttributes } from 'svelte/elements';
|
|
|
3
3
|
export interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
|
|
4
4
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
5
5
|
variant?: 'default' | 'display';
|
|
6
|
+
/**
|
|
7
|
+
* Caps the rendered inline size in ch units so editorial headlines wrap on
|
|
8
|
+
* ergonomic measure. `false` (default) keeps the existing behaviour of no
|
|
9
|
+
* cap. `narrow` ≈ 22ch, `default` ≈ 45ch, `wide` ≈ 65ch.
|
|
10
|
+
*/
|
|
11
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
6
12
|
className?: HTMLAttributes<HTMLHeadingElement>['class'];
|
|
7
13
|
children: Snippet;
|
|
8
14
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -221,7 +221,7 @@ export type { ButtonGroupProps } from './button-group/index.js';
|
|
|
221
221
|
export { Chip } from './chip/index.js';
|
|
222
222
|
export type { ChipProps, ChipColor } from './chip/index.js';
|
|
223
223
|
export { ChipGroup } from './chip-group/index.js';
|
|
224
|
-
export type { ChipGroupRootProps, ChipGroupItemProps } from './chip-group/index.js';
|
|
224
|
+
export type { ChipGroupRootProps, ChipGroupItemProps, ChipGroupLabelProps } from './chip-group/index.js';
|
|
225
225
|
export { Carousel } from './carousel/index.js';
|
|
226
226
|
export type { CarouselRootProps, CarouselViewportProps, CarouselSlideProps, CarouselPrevProps, CarouselNextProps, CarouselDotsProps } from './carousel/index.js';
|
|
227
227
|
export { FormatBytes } from './format-bytes/index.js';
|
package/dist/text/index.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface TextProps extends HTMLAttributes<HTMLElement> {
|
|
|
7
7
|
font?: 'sans' | 'mono';
|
|
8
8
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
9
9
|
variant?: 'default' | 'label';
|
|
10
|
+
/**
|
|
11
|
+
* Caps the rendered inline size in ch units so body copy wraps on ergonomic
|
|
12
|
+
* measure. `false` (default) keeps the existing behaviour of no cap.
|
|
13
|
+
* `narrow` ≈ 48ch, `default` ≈ 65ch, `wide` ≈ 80ch.
|
|
14
|
+
*/
|
|
15
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
10
16
|
className?: HTMLAttributes<HTMLElement>['class'];
|
|
11
17
|
children: Snippet;
|
|
12
18
|
}
|
package/dist/text/text.svelte
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
font?: 'sans' | 'mono';
|
|
11
11
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
12
12
|
variant?: 'default' | 'label';
|
|
13
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
13
14
|
className?: HTMLAttributes<HTMLElement>['class'];
|
|
14
15
|
children: Snippet;
|
|
15
16
|
}
|
|
@@ -21,23 +22,28 @@
|
|
|
21
22
|
font = 'sans',
|
|
22
23
|
weight,
|
|
23
24
|
variant = 'default',
|
|
25
|
+
maxMeasure = false,
|
|
24
26
|
class: classAttr,
|
|
25
27
|
className = classAttr,
|
|
26
28
|
children,
|
|
27
29
|
...rest
|
|
28
30
|
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
let measure: 'narrow' | 'default' | 'wide' | undefined = $derived(
|
|
33
|
+
maxMeasure === false ? undefined : maxMeasure
|
|
34
|
+
);
|
|
29
35
|
</script>
|
|
30
36
|
|
|
31
37
|
{#if as === 'span'}
|
|
32
38
|
<span
|
|
33
39
|
class={className}
|
|
34
|
-
{...variantAttrs({ color, size, font, weight: weight || undefined, variant })}
|
|
40
|
+
{...variantAttrs({ color, size, font, weight: weight || undefined, variant, measure })}
|
|
35
41
|
{...rest}>{@render children()}</span
|
|
36
42
|
>
|
|
37
43
|
{:else if as === 'div'}
|
|
38
44
|
<div
|
|
39
45
|
class={className}
|
|
40
|
-
{...variantAttrs({ color, size, font, weight: weight || undefined, variant })}
|
|
46
|
+
{...variantAttrs({ color, size, font, weight: weight || undefined, variant, measure })}
|
|
41
47
|
{...rest}
|
|
42
48
|
>
|
|
43
49
|
{@render children()}
|
|
@@ -45,7 +51,7 @@
|
|
|
45
51
|
{:else}
|
|
46
52
|
<p
|
|
47
53
|
class={className}
|
|
48
|
-
{...variantAttrs({ color, size, font, weight: weight || undefined, variant })}
|
|
54
|
+
{...variantAttrs({ color, size, font, weight: weight || undefined, variant, measure })}
|
|
49
55
|
{...rest}
|
|
50
56
|
>
|
|
51
57
|
{@render children()}
|
|
@@ -132,4 +138,19 @@
|
|
|
132
138
|
letter-spacing: 0.05em;
|
|
133
139
|
font-weight: 600;
|
|
134
140
|
}
|
|
141
|
+
|
|
142
|
+
/* ── Measure (max-inline-size) ─────────────────────────────────────────────
|
|
143
|
+
Body copy gets wider presets than Heading so paragraphs read well
|
|
144
|
+
without feeling cramped. */
|
|
145
|
+
[data-measure='narrow'] {
|
|
146
|
+
max-inline-size: 48ch;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[data-measure='default'] {
|
|
150
|
+
max-inline-size: 65ch;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
[data-measure='wide'] {
|
|
154
|
+
max-inline-size: 80ch;
|
|
155
|
+
}
|
|
135
156
|
</style>
|
|
@@ -7,6 +7,7 @@ interface Props extends HTMLAttributes<HTMLElement> {
|
|
|
7
7
|
font?: 'sans' | 'mono';
|
|
8
8
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
9
9
|
variant?: 'default' | 'label';
|
|
10
|
+
maxMeasure?: 'narrow' | 'default' | 'wide' | false;
|
|
10
11
|
className?: HTMLAttributes<HTMLElement>['class'];
|
|
11
12
|
children: Snippet;
|
|
12
13
|
}
|
package/dist/themes/dark.css
CHANGED
|
@@ -217,7 +217,15 @@
|
|
|
217
217
|
--dry-grain-blend: soft-light;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
/* When `.theme-auto` is combined with `data-theme="light"`, the explicit
|
|
221
|
+
`data-theme` wins — do NOT apply dark tokens under that combo, even on a
|
|
222
|
+
dark-preferring OS. Without this guard, `<html class="theme-auto" data-theme="light">`
|
|
223
|
+
on a dark OS would still resolve to dark tokens because the media query
|
|
224
|
+
below fires regardless of the attribute. Explicit `data-theme="dark"` is
|
|
225
|
+
already handled by the rule above; this class only drives the system-aware
|
|
226
|
+
auto behaviour when no explicit override is present (or when the override is
|
|
227
|
+
explicitly dark). */
|
|
228
|
+
.theme-auto:not([data-theme='light']) {
|
|
221
229
|
@media (prefers-color-scheme: dark) {
|
|
222
230
|
& {
|
|
223
231
|
/* ─── Neutral ─────────────────────────────────────────────────── */
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
let {
|
|
6
6
|
level = 2,
|
|
7
7
|
variant = 'default',
|
|
8
|
+
maxMeasure = false,
|
|
8
9
|
class: className,
|
|
9
10
|
children,
|
|
10
11
|
...rest
|
|
11
12
|
}: HeadingProps = $props();
|
|
12
13
|
</script>
|
|
13
14
|
|
|
14
|
-
<Heading {level} {variant} {className} {...rest}>
|
|
15
|
+
<Heading {level} {variant} {maxMeasure} {className} {...rest}>
|
|
15
16
|
{@render children()}
|
|
16
17
|
</Heading>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
size = 'md',
|
|
10
10
|
font = 'sans',
|
|
11
11
|
weight,
|
|
12
|
+
maxMeasure = false,
|
|
12
13
|
class: className,
|
|
13
14
|
children,
|
|
14
15
|
...rest
|
|
@@ -20,6 +21,16 @@
|
|
|
20
21
|
let textVariant: 'default' | 'label' = $derived(variant === 'label' ? 'label' : 'default');
|
|
21
22
|
</script>
|
|
22
23
|
|
|
23
|
-
<Text
|
|
24
|
+
<Text
|
|
25
|
+
{as}
|
|
26
|
+
color={tone}
|
|
27
|
+
{size}
|
|
28
|
+
{font}
|
|
29
|
+
{weight}
|
|
30
|
+
variant={textVariant}
|
|
31
|
+
{maxMeasure}
|
|
32
|
+
{className}
|
|
33
|
+
{...rest}
|
|
34
|
+
>
|
|
24
35
|
{@render children()}
|
|
25
36
|
</Text>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dryui/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Zero-dependency styled Svelte 5 components with scoped styles and --dry-* CSS variable theming.",
|
|
5
5
|
"author": "Rob Balfre",
|
|
6
6
|
"license": "MIT",
|
|
@@ -784,16 +784,16 @@
|
|
|
784
784
|
"postpack": "bun ../../scripts/postpack-exports.ts"
|
|
785
785
|
},
|
|
786
786
|
"dependencies": {
|
|
787
|
-
"@dryui/primitives": "^1.
|
|
787
|
+
"@dryui/primitives": "^1.8.0"
|
|
788
788
|
},
|
|
789
789
|
"peerDependencies": {
|
|
790
|
-
"svelte": "^5.55.
|
|
790
|
+
"svelte": "^5.55.4"
|
|
791
791
|
},
|
|
792
792
|
"devDependencies": {
|
|
793
|
-
"@dryui/lint": "^0.
|
|
794
|
-
"svelte": "^5.55.
|
|
793
|
+
"@dryui/lint": "^0.5.0",
|
|
794
|
+
"svelte": "^5.55.4",
|
|
795
795
|
"@sveltejs/package": "^2.5.7",
|
|
796
796
|
"svelte-check": "^4.4.6",
|
|
797
|
-
"typescript": "^6.0.
|
|
797
|
+
"typescript": "^6.0.3"
|
|
798
798
|
}
|
|
799
799
|
}
|
|
@@ -92,12 +92,8 @@ Use AlertDialog (not Dialog) for destructive confirmations. It requires explicit
|
|
|
92
92
|
<p>This action cannot be undone.</p>
|
|
93
93
|
</AlertDialog.Body>
|
|
94
94
|
<AlertDialog.Footer>
|
|
95
|
-
<AlertDialog.Cancel>
|
|
96
|
-
|
|
97
|
-
</AlertDialog.Cancel>
|
|
98
|
-
<AlertDialog.Action>
|
|
99
|
-
<Button variant="solid">Delete</Button>
|
|
100
|
-
</AlertDialog.Action>
|
|
95
|
+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
|
|
96
|
+
<AlertDialog.Action>Delete</AlertDialog.Action>
|
|
101
97
|
</AlertDialog.Footer>
|
|
102
98
|
</AlertDialog.Content>
|
|
103
99
|
</AlertDialog.Root>
|
|
@@ -144,12 +144,8 @@ Use AlertDialog for destructive confirmations. It traps focus and requires expli
|
|
|
144
144
|
<p>This action cannot be undone.</p>
|
|
145
145
|
</AlertDialog.Body>
|
|
146
146
|
<AlertDialog.Footer>
|
|
147
|
-
<AlertDialog.Cancel>
|
|
148
|
-
|
|
149
|
-
</AlertDialog.Cancel>
|
|
150
|
-
<AlertDialog.Action>
|
|
151
|
-
<Button variant="solid">Delete</Button>
|
|
152
|
-
</AlertDialog.Action>
|
|
147
|
+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
|
|
148
|
+
<AlertDialog.Action>Delete</AlertDialog.Action>
|
|
153
149
|
</AlertDialog.Footer>
|
|
154
150
|
</AlertDialog.Content>
|
|
155
151
|
</AlertDialog.Root>
|
|
@@ -159,6 +159,26 @@ Force a specific theme:
|
|
|
159
159
|
|
|
160
160
|
Use `data-theme="light"` for the inverse.
|
|
161
161
|
|
|
162
|
+
### Option B.1: Light-only sites
|
|
163
|
+
|
|
164
|
+
For a site that should always render light (brand / marketing / docs), combine
|
|
165
|
+
both attributes on `<html>`:
|
|
166
|
+
|
|
167
|
+
```html
|
|
168
|
+
<html class="theme-auto" data-theme="light"></html>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- `data-theme="light"` pins the page to light tokens even on a dark-preferring
|
|
172
|
+
OS. The `.theme-auto` dark block is guarded with
|
|
173
|
+
`:not([data-theme='light'])`, so a dark OS cannot silently override your
|
|
174
|
+
light design.
|
|
175
|
+
- `class="theme-auto"` stays so an opt-in dark toggle (flipping `data-theme`
|
|
176
|
+
to `"dark"`) keeps working. Drop `theme-auto` entirely only if you never
|
|
177
|
+
want a dark pathway, and the explicit `data-theme='dark']` rule is enough
|
|
178
|
+
on its own.
|
|
179
|
+
|
|
180
|
+
Use `ask --scope recipe "light only"` for the full scaffold.
|
|
181
|
+
|
|
162
182
|
### Option C: Persisted theme toggle
|
|
163
183
|
|
|
164
184
|
If you add a theme switcher, keep system mode as the default and only store explicit user choices:
|