@sentropic/design-system-svelte 0.9.0 → 0.10.1

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 (68) hide show
  1. package/dist/AreaChart.svelte +6 -0
  2. package/dist/Button.svelte +59 -20
  3. package/dist/Card.svelte +13 -4
  4. package/dist/Checkbox.svelte +26 -3
  5. package/dist/Checkbox.svelte.d.ts.map +1 -1
  6. package/dist/Combobox.svelte +17 -6
  7. package/dist/DatePicker.svelte +17 -6
  8. package/dist/DonutChart.svelte +169 -0
  9. package/dist/DonutChart.svelte.d.ts +21 -0
  10. package/dist/DonutChart.svelte.d.ts.map +1 -0
  11. package/dist/Footer.svelte +124 -0
  12. package/dist/Footer.svelte.d.ts +21 -0
  13. package/dist/Footer.svelte.d.ts.map +1 -0
  14. package/dist/Highlight.svelte +63 -0
  15. package/dist/Highlight.svelte.d.ts +15 -0
  16. package/dist/Highlight.svelte.d.ts.map +1 -0
  17. package/dist/Input.svelte +50 -13
  18. package/dist/LanguageSelector.svelte +181 -0
  19. package/dist/LanguageSelector.svelte.d.ts +18 -0
  20. package/dist/LanguageSelector.svelte.d.ts.map +1 -0
  21. package/dist/Link.svelte +24 -10
  22. package/dist/MessageStatusBadge.svelte +11 -7
  23. package/dist/MessageStatusBadge.svelte.d.ts +2 -0
  24. package/dist/MessageStatusBadge.svelte.d.ts.map +1 -1
  25. package/dist/MultiSelect.svelte +17 -7
  26. package/dist/NumberInput.svelte +17 -6
  27. package/dist/OrderedList.svelte +103 -0
  28. package/dist/OrderedList.svelte.d.ts +17 -0
  29. package/dist/OrderedList.svelte.d.ts.map +1 -0
  30. package/dist/OverflowMenu.svelte +5 -2
  31. package/dist/PaginationNav.svelte +5 -2
  32. package/dist/PasswordInput.svelte +17 -6
  33. package/dist/Quote.svelte +72 -0
  34. package/dist/Quote.svelte.d.ts +16 -0
  35. package/dist/Quote.svelte.d.ts.map +1 -0
  36. package/dist/Radio.svelte +29 -1
  37. package/dist/ScatterPlot.svelte +179 -0
  38. package/dist/ScatterPlot.svelte.d.ts +21 -0
  39. package/dist/ScatterPlot.svelte.d.ts.map +1 -0
  40. package/dist/Search.svelte +17 -6
  41. package/dist/Select.svelte +57 -9
  42. package/dist/SkipLink.svelte +50 -0
  43. package/dist/SkipLink.svelte.d.ts +12 -0
  44. package/dist/SkipLink.svelte.d.ts.map +1 -0
  45. package/dist/StackedBarChart.svelte +190 -0
  46. package/dist/StackedBarChart.svelte.d.ts +22 -0
  47. package/dist/StackedBarChart.svelte.d.ts.map +1 -0
  48. package/dist/StreamingMessage.svelte +47 -0
  49. package/dist/StreamingMessage.svelte.d.ts +7 -0
  50. package/dist/StreamingMessage.svelte.d.ts.map +1 -1
  51. package/dist/Switch.svelte +5 -1
  52. package/dist/Tabs.svelte +37 -8
  53. package/dist/Textarea.svelte +47 -9
  54. package/dist/Tile.svelte +165 -0
  55. package/dist/Tile.svelte.d.ts +24 -0
  56. package/dist/Tile.svelte.d.ts.map +1 -0
  57. package/dist/Toggle.svelte +5 -1
  58. package/dist/Toggletip.svelte +5 -2
  59. package/dist/TreeView.svelte +213 -0
  60. package/dist/TreeView.svelte.d.ts +20 -0
  61. package/dist/TreeView.svelte.d.ts.map +1 -0
  62. package/dist/UnorderedList.svelte +4 -3
  63. package/dist/UnorderedList.svelte.d.ts +3 -2
  64. package/dist/UnorderedList.svelte.d.ts.map +1 -1
  65. package/dist/index.d.ts +19 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +11 -0
  68. package/package.json +4 -2
@@ -63,8 +63,12 @@
63
63
  }
64
64
 
65
65
  .st-field__label {
66
- font-size: 0.875rem;
67
- font-weight: 600;
66
+ font-family: var(--st-component-field-labelTypography-family, inherit);
67
+ font-size: var(--st-component-field-labelTypography-size, 0.875rem);
68
+ font-weight: var(--st-component-field-labelTypography-weight, 600);
69
+ line-height: var(--st-component-field-labelTypography-lineHeight, 1.4);
70
+ letter-spacing: var(--st-component-field-labelTypography-letterSpacing, 0);
71
+ text-transform: var(--st-component-field-labelTypography-textTransform, none);
68
72
  }
69
73
 
70
74
  .st-field__help,
@@ -81,14 +85,54 @@
81
85
  color: var(--st-component-field-errorText, var(--st-semantic-feedback-error));
82
86
  }
83
87
 
88
+ /* Field box = resolved field anatomy (v1.2.0), same as Input. */
84
89
  .st-select {
85
- background: var(--st-component-control-background, var(--st-semantic-surface-default));
86
- border: 1px solid var(--st-component-control-border, var(--st-semantic-border-subtle));
87
- border-radius: var(--st-component-control-radius, 0.375rem);
90
+ /* Native <select> rendering (anatomy v1.4.0, F5/F9). A native <select> with
91
+ `appearance: auto` has its `line-height` FORCED to `normal` by the browser
92
+ (the anatomy line-height below never lands); `appearance: none` lets it
93
+ take effect — the real DSFR/Carbon selects use `appearance: none` + a
94
+ drawn chevron, which is why they render 24px / 18px. Base = `auto` so the
95
+ Sent Tech select keeps its NATIVE arrow + render (unchanged). When a theme
96
+ opts into `none`, `selectChevron` redraws the arrow the UA dropped. */
97
+ appearance: var(--st-component-control-anatomy-field-selectAppearance, auto);
98
+ -webkit-appearance: var(--st-component-control-anatomy-field-selectAppearance, auto);
99
+ background:
100
+ var(--st-component-control-anatomy-field-selectChevron, none),
101
+ var(--st-component-control-anatomy-field-fillBg, var(--st-component-control-background, var(--st-semantic-surface-default)));
102
+ border-top: var(--st-component-control-anatomy-field-borderTop, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
103
+ border-right: var(--st-component-control-anatomy-field-borderRight, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
104
+ border-bottom: var(--st-component-control-anatomy-field-borderBottom, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
105
+ border-left: var(--st-component-control-anatomy-field-borderLeft, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
106
+ /* Per-corner radius + box-shadow underline (anatomy v1.3.0) — same field
107
+ anatomy as Input. TOP corners = radiusTop (DSFR 4px), bottom = shape.radius;
108
+ bottom rule via inset box-shadow. Fallbacks reproduce the prior uniform
109
+ rounded box with no underline shadow → base Sent Tech unchanged. */
110
+ border-top-left-radius: var(--st-component-control-anatomy-field-radiusTop, var(--st-component-control-anatomy-shape-radius, 0.375rem));
111
+ border-top-right-radius: var(--st-component-control-anatomy-field-radiusTop, var(--st-component-control-anatomy-shape-radius, 0.375rem));
112
+ border-bottom-right-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
113
+ border-bottom-left-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
114
+ box-shadow: var(--st-component-control-anatomy-field-underline, none);
88
115
  color: var(--st-component-control-text, var(--st-semantic-text-primary));
89
- font: inherit;
116
+ font-family: var(--st-component-control-anatomy-typography-family, inherit);
117
+ font-size: var(--st-component-control-anatomy-typography-size, inherit);
118
+ font-weight: var(--st-component-control-anatomy-typography-weight, 400);
119
+ /* Native <select> renders `line-height: normal` unless one is set explicitly;
120
+ pose the anatomy line-height so DSFR/Carbon select matches the field roles
121
+ (cluster 4). letter-spacing likewise (Carbon 0.16px). Additive — base keeps
122
+ its 1.5 fallback so the Sent Tech select is unchanged. */
123
+ line-height: var(--st-component-control-anatomy-typography-lineHeight, 1.5);
124
+ letter-spacing: var(--st-component-control-anatomy-typography-letterSpacing, normal);
90
125
  min-width: 0;
91
- padding: 0 2rem 0 0.75rem;
126
+ /* Padding follows the field density (additive; fallbacks reproduce the prior
127
+ `0 2rem 0 0.75rem` literal so the base Sent Tech select is unchanged):
128
+ vertical = md paddingBlock (base 0 → DSFR 8px), left = sm paddingInline
129
+ (base 0.75rem → DSFR/Carbon 16px). The right side reserves the chevron
130
+ gutter via selectPaddingRight (F9: base 2rem → DSFR 40px / Carbon 48px). */
131
+ padding:
132
+ var(--st-component-control-anatomy-density-md-paddingBlock, 0)
133
+ var(--st-component-control-anatomy-field-selectPaddingRight, 2rem)
134
+ var(--st-component-control-anatomy-density-md-paddingBlock, 0)
135
+ var(--st-component-control-anatomy-density-sm-paddingInline, 0.75rem);
92
136
  width: 100%;
93
137
  }
94
138
 
@@ -106,8 +150,12 @@
106
150
 
107
151
  .st-select:focus-visible {
108
152
  border-color: var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
109
- box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
110
- outline: none;
153
+ outline: var(--st-component-control-anatomy-focus-outline, none);
154
+ outline-offset: var(--st-component-control-anatomy-focus-offset, 0);
155
+ /* Composed field focus box-shadow (v1.3.0): keeps the resting underline. */
156
+ box-shadow: var(--st-component-control-anatomy-field-focusShadow,
157
+ var(--st-component-control-anatomy-focus-boxShadow,
158
+ 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive))));
111
159
  }
112
160
 
113
161
  .st-select[aria-invalid="true"] {
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAnchorAttributes } from "svelte/elements";
4
+
5
+ type SkipLinkProps = Omit<HTMLAnchorAttributes, "class" | "href"> & {
6
+ /** Cible de l'ancre (id du contenu principal). */
7
+ href?: string;
8
+ class?: string;
9
+ children?: Snippet;
10
+ };
11
+
12
+ let {
13
+ href = "#main-content",
14
+ class: className,
15
+ children,
16
+ ...rest
17
+ }: SkipLinkProps = $props();
18
+
19
+ const classes = () => ["st-skipLink", className].filter(Boolean).join(" ");
20
+ </script>
21
+
22
+ <a {...rest} class={classes()} {href}>
23
+ {#if children}{@render children()}{:else}Aller au contenu principal{/if}
24
+ </a>
25
+
26
+ <style>
27
+ /* Lien d'évitement : hors écran jusqu'au focus clavier (a11y, DSFR/WCAG). */
28
+ .st-skipLink {
29
+ background: var(--st-semantic-action-primary);
30
+ border-radius: var(--st-radius-md, 0.375rem);
31
+ color: var(--st-semantic-action-primaryText, #fff);
32
+ font-size: 0.875rem;
33
+ font-weight: 600;
34
+ left: var(--st-spacing-2, 0.5rem);
35
+ padding: var(--st-spacing-2, 0.5rem) var(--st-spacing-4, 1rem);
36
+ position: fixed;
37
+ text-decoration: none;
38
+ top: var(--st-spacing-2, 0.5rem);
39
+ transform: translateY(-150%);
40
+ transition: transform var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
41
+ z-index: var(--st-zindex-modal, 100);
42
+ }
43
+
44
+ .st-skipLink:focus,
45
+ .st-skipLink:focus-visible {
46
+ outline: 2px solid var(--st-semantic-border-interactive);
47
+ outline-offset: 2px;
48
+ transform: translateY(0);
49
+ }
50
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAnchorAttributes } from "svelte/elements";
3
+ type SkipLinkProps = Omit<HTMLAnchorAttributes, "class" | "href"> & {
4
+ /** Cible de l'ancre (id du contenu principal). */
5
+ href?: string;
6
+ class?: string;
7
+ children?: Snippet;
8
+ };
9
+ declare const SkipLink: import("svelte").Component<SkipLinkProps, {}, "">;
10
+ type SkipLink = ReturnType<typeof SkipLink>;
11
+ export default SkipLink;
12
+ //# sourceMappingURL=SkipLink.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkipLink.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SkipLink.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,KAAK,aAAa,GAAG,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG;IAClE,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAwBJ,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,190 @@
1
+ <script lang="ts" module>
2
+ export type StackedBarTone =
3
+ | "category1" | "category2" | "category3" | "category4"
4
+ | "category5" | "category6" | "category7" | "category8";
5
+
6
+ export type StackedBarSegment = {
7
+ label: string;
8
+ value: number;
9
+ tone?: StackedBarTone;
10
+ };
11
+
12
+ export type StackedBarDatum = {
13
+ label: string;
14
+ segments: StackedBarSegment[];
15
+ };
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ type StackedBarChartProps = {
20
+ data: StackedBarDatum[];
21
+ width?: number;
22
+ height?: number;
23
+ label: string;
24
+ showLegend?: boolean;
25
+ class?: string;
26
+ };
27
+
28
+ let {
29
+ data,
30
+ width = 480,
31
+ height = 260,
32
+ label,
33
+ showLegend = true,
34
+ class: className
35
+ }: StackedBarChartProps = $props();
36
+
37
+ const MARGIN = { top: 14, right: 16, bottom: 34, left: 44 };
38
+ const TONES = ["category1","category2","category3","category4","category5","category6","category7","category8"] as const;
39
+
40
+ function niceTicks(min: number, max: number, target = 5): number[] {
41
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) return [Number.isFinite(max) ? max : 0];
42
+ const range = max - min;
43
+ const rough = range / Math.max(target - 1, 1);
44
+ const pow = Math.pow(10, Math.floor(Math.log10(rough)));
45
+ const norm = rough / pow;
46
+ const step = norm < 1.5 ? pow : norm < 3 ? 2 * pow : norm < 7 ? 5 * pow : 10 * pow;
47
+ const start = Math.floor(min / step) * step;
48
+ const end = Math.ceil(max / step) * step;
49
+ const ticks: number[] = [];
50
+ for (let v = start; v <= end + step / 2; v += step) ticks.push(Number(v.toFixed(10)));
51
+ return ticks;
52
+ }
53
+ const scaleLinear = (v: number, d0: number, d1: number, r0: number, r1: number) =>
54
+ d1 === d0 ? r0 : r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
55
+ const fmt = (v: number) => (Math.abs(v) >= 1000 ? `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k` : Number.isInteger(v) ? String(v) : v.toFixed(1));
56
+
57
+ // Légende : tones par label de série (ordre de la 1re barre).
58
+ const legend = $derived.by(() => {
59
+ const seen = new Map<string, StackedBarTone>();
60
+ data.forEach((bar) => bar.segments.forEach((seg, i) => {
61
+ if (!seen.has(seg.label)) seen.set(seg.label, seg.tone ?? TONES[i % TONES.length]);
62
+ }));
63
+ return [...seen.entries()].map(([seriesLabel, tone]) => ({ seriesLabel, tone }));
64
+ });
65
+
66
+ let hovered: { bar: number; seg: number } | null = $state(null);
67
+
68
+ const scales = $derived.by(() => {
69
+ const totals = data.map((b) => b.segments.reduce((s, x) => s + Math.max(x.value, 0), 0));
70
+ const ticks = niceTicks(0, Math.max(0, ...totals));
71
+ return {
72
+ ticks, domainMax: ticks[ticks.length - 1],
73
+ plotW: Math.max(width - MARGIN.left - MARGIN.right, 1),
74
+ plotH: Math.max(height - MARGIN.top - MARGIN.bottom, 1)
75
+ };
76
+ });
77
+
78
+ const bars = $derived.by(() => {
79
+ const { domainMax, plotW, plotH } = scales;
80
+ if (data.length === 0) return [];
81
+ const band = plotW / data.length;
82
+ const barWidth = band * 0.6;
83
+ return data.map((bar, bi) => {
84
+ const x = MARGIN.left + band * bi + (band - barWidth) / 2;
85
+ let acc = 0;
86
+ const segs = bar.segments.map((seg, si) => {
87
+ const v = Math.max(seg.value, 0);
88
+ const yTop = MARGIN.top + scaleLinear(acc + v, 0, domainMax, plotH, 0);
89
+ const yBottom = MARGIN.top + scaleLinear(acc, 0, domainMax, plotH, 0);
90
+ acc += v;
91
+ return {
92
+ x, y: yTop, width: barWidth, height: Math.max(yBottom - yTop, 0),
93
+ seg, tone: seg.tone ?? TONES[si % TONES.length],
94
+ cx: x + barWidth / 2, cy: yTop + (yBottom - yTop) / 2
95
+ };
96
+ });
97
+ return { x, band, label: bar.label, segs, cxLabel: MARGIN.left + band * (bi + 0.5) };
98
+ });
99
+ });
100
+
101
+ const classes = () => ["st-stackedBar", className].filter(Boolean).join(" ");
102
+ </script>
103
+
104
+ <div class={classes()} role="img" aria-label={label}>
105
+ <svg viewBox="0 0 {width} {height}" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" focusable="false" aria-hidden="true">
106
+ {#each scales.ticks as t (t)}
107
+ {@const y = MARGIN.top + scaleLinear(t, 0, scales.domainMax, scales.plotH, 0)}
108
+ <line class="st-stackedBar__grid" x1={MARGIN.left} x2={width - MARGIN.right} y1={y} y2={y} />
109
+ <text class="st-stackedBar__tick" x={MARGIN.left - 6} y={y} text-anchor="end" dominant-baseline="middle">{fmt(t)}</text>
110
+ {/each}
111
+
112
+ <line class="st-stackedBar__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
113
+ <line class="st-stackedBar__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
114
+
115
+ {#each bars as bar, bi (bar.label)}
116
+ <text class="st-stackedBar__categoryLabel" x={bar.cxLabel} y={height - MARGIN.bottom + 16} text-anchor="middle">{bar.label}</text>
117
+ {#each bar.segs as s, si (s.seg.label)}
118
+ <rect
119
+ class="st-stackedBar__seg st-stackedBar__seg--{s.tone}"
120
+ class:st-stackedBar__seg--dim={hovered !== null && !(hovered.bar === bi && hovered.seg === si)}
121
+ x={s.x} y={s.y} width={s.width} height={s.height}
122
+ tabindex="0"
123
+ role="img"
124
+ aria-label="{bar.label} — {s.seg.label}: {s.seg.value}"
125
+ onmouseenter={() => (hovered = { bar: bi, seg: si })}
126
+ onmouseleave={() => (hovered = null)}
127
+ onfocus={() => (hovered = { bar: bi, seg: si })}
128
+ onblur={() => (hovered = null)}
129
+ />
130
+ {/each}
131
+ {/each}
132
+ </svg>
133
+
134
+ {#if hovered && bars[hovered.bar]?.segs[hovered.seg]}
135
+ {@const s = bars[hovered.bar].segs[hovered.seg]}
136
+ <div class="st-stackedBar__tooltip" role="presentation" style="left: {(s.cx / width) * 100}%; top: {(s.cy / height) * 100}%">
137
+ <span class="st-stackedBar__tooltipLabel">{s.seg.label}</span>
138
+ <span class="st-stackedBar__tooltipValue">{s.seg.value}</span>
139
+ </div>
140
+ {/if}
141
+
142
+ {#if showLegend && legend.length > 0}
143
+ <ul class="st-stackedBar__legend">
144
+ {#each legend as item (item.seriesLabel)}
145
+ <li class="st-stackedBar__legendItem">
146
+ <span class="st-stackedBar__legendSwatch st-stackedBar__legendSwatch--{item.tone}" aria-hidden="true"></span>
147
+ {item.seriesLabel}
148
+ </li>
149
+ {/each}
150
+ </ul>
151
+ {/if}
152
+ </div>
153
+
154
+ <style>
155
+ .st-stackedBar { color: var(--st-semantic-text-secondary); display: block; font-family: inherit; position: relative; width: 100%; }
156
+ .st-stackedBar svg { display: block; overflow: visible; }
157
+ .st-stackedBar__grid { stroke: var(--st-semantic-border-subtle); stroke-dasharray: 2 3; stroke-width: 1; opacity: 0.7; }
158
+ .st-stackedBar__axis { stroke: var(--st-semantic-border-subtle); stroke-width: 1; }
159
+ .st-stackedBar__tick, .st-stackedBar__categoryLabel { fill: var(--st-semantic-text-secondary); font-size: 0.6875rem; }
160
+ .st-stackedBar__seg { cursor: pointer; stroke: var(--st-semantic-surface-default, #fff); stroke-width: 1; transition: opacity 120ms ease; }
161
+ .st-stackedBar__seg--dim { opacity: 0.45; }
162
+ .st-stackedBar__seg:focus-visible { outline: 2px solid var(--st-semantic-border-interactive); outline-offset: 1px; }
163
+ .st-stackedBar__seg--category1 { fill: var(--st-semantic-data-category1); }
164
+ .st-stackedBar__seg--category2 { fill: var(--st-semantic-data-category2); }
165
+ .st-stackedBar__seg--category3 { fill: var(--st-semantic-data-category3); }
166
+ .st-stackedBar__seg--category4 { fill: var(--st-semantic-data-category4); }
167
+ .st-stackedBar__seg--category5 { fill: var(--st-semantic-data-category5); }
168
+ .st-stackedBar__seg--category6 { fill: var(--st-semantic-data-category6); }
169
+ .st-stackedBar__seg--category7 { fill: var(--st-semantic-data-category7); }
170
+ .st-stackedBar__seg--category8 { fill: var(--st-semantic-data-category8); }
171
+ .st-stackedBar__tooltip {
172
+ background: var(--st-semantic-surface-inverse); border-radius: var(--st-radius-sm, 0.25rem);
173
+ color: var(--st-semantic-text-inverse); display: inline-flex; flex-direction: column; font-size: 0.75rem;
174
+ gap: 0.125rem; line-height: 1.2; padding: 0.375rem 0.5rem; pointer-events: none; position: absolute;
175
+ transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap; z-index: 1;
176
+ }
177
+ .st-stackedBar__tooltipLabel { font-weight: 600; }
178
+ .st-stackedBar__tooltipValue { opacity: 0.85; }
179
+ .st-stackedBar__legend { display: flex; flex-wrap: wrap; gap: 0.75rem; list-style: none; margin: 0.5rem 0 0; padding: 0; }
180
+ .st-stackedBar__legendItem { align-items: center; color: var(--st-semantic-text-secondary); display: inline-flex; font-size: 0.75rem; gap: 0.35rem; }
181
+ .st-stackedBar__legendSwatch { border-radius: 2px; height: 0.7rem; width: 0.7rem; }
182
+ .st-stackedBar__legendSwatch--category1 { background: var(--st-semantic-data-category1); }
183
+ .st-stackedBar__legendSwatch--category2 { background: var(--st-semantic-data-category2); }
184
+ .st-stackedBar__legendSwatch--category3 { background: var(--st-semantic-data-category3); }
185
+ .st-stackedBar__legendSwatch--category4 { background: var(--st-semantic-data-category4); }
186
+ .st-stackedBar__legendSwatch--category5 { background: var(--st-semantic-data-category5); }
187
+ .st-stackedBar__legendSwatch--category6 { background: var(--st-semantic-data-category6); }
188
+ .st-stackedBar__legendSwatch--category7 { background: var(--st-semantic-data-category7); }
189
+ .st-stackedBar__legendSwatch--category8 { background: var(--st-semantic-data-category8); }
190
+ </style>
@@ -0,0 +1,22 @@
1
+ export type StackedBarTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type StackedBarSegment = {
3
+ label: string;
4
+ value: number;
5
+ tone?: StackedBarTone;
6
+ };
7
+ export type StackedBarDatum = {
8
+ label: string;
9
+ segments: StackedBarSegment[];
10
+ };
11
+ type StackedBarChartProps = {
12
+ data: StackedBarDatum[];
13
+ width?: number;
14
+ height?: number;
15
+ label: string;
16
+ showLegend?: boolean;
17
+ class?: string;
18
+ };
19
+ declare const StackedBarChart: import("svelte").Component<StackedBarChartProps, {}, "">;
20
+ type StackedBarChart = ReturnType<typeof StackedBarChart>;
21
+ export default StackedBarChart;
22
+ //# sourceMappingURL=StackedBarChart.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StackedBarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StackedBarChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6HJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -9,6 +9,8 @@
9
9
  export type StreamingMessageEvent =
10
10
  | { type: "message.delta"; messageId?: string; delta: string }
11
11
  | { type: "message.completed"; messageId?: string }
12
+ | { type: "reasoning.delta"; messageId?: string; delta: string }
13
+ | { type: "reasoning.completed"; messageId?: string }
12
14
  | { type: "tool.started"; toolCallId: string; toolName: string; messageId?: string }
13
15
  | {
14
16
  type: "tool.completed";
@@ -84,6 +86,15 @@
84
86
  .join("");
85
87
  };
86
88
 
89
+ const resolvedReasoning = () =>
90
+ mergedEvents()
91
+ .filter((event) => event.type === "reasoning.delta")
92
+ .map((event) => event.delta)
93
+ .join("");
94
+
95
+ const reasoningCompleted = () =>
96
+ mergedEvents().some((event) => event.type === "reasoning.completed");
97
+
87
98
  const hasAnyEvent = () => mergedEvents().length > 0;
88
99
 
89
100
  const toolCalls = (): ToolCallState[] => {
@@ -129,6 +140,10 @@
129
140
  return "message.delta";
130
141
  case "message.completed":
131
142
  return "message.completed";
143
+ case "reasoning.delta":
144
+ return "reasoning.delta";
145
+ case "reasoning.completed":
146
+ return "reasoning.completed";
132
147
  case "tool.started":
133
148
  return `${event.toolName} démarré (${event.toolCallId})`;
134
149
  case "tool.completed":
@@ -155,6 +170,15 @@
155
170
  footer={footer}
156
171
  actions={actions}
157
172
  >
173
+ {#if resolvedReasoning()}
174
+ <details class="st-streamingMessage__reasoning" open={!reasoningCompleted()}>
175
+ <summary class="st-streamingMessage__reasoningToggle">
176
+ Raisonnement{reasoningCompleted() ? "" : "…"}
177
+ </summary>
178
+ <p class="st-streamingMessage__reasoningText">{resolvedReasoning()}</p>
179
+ </details>
180
+ {/if}
181
+
158
182
  {#if resolvedContent()}
159
183
  <p class="st-streamingMessage__text">{resolvedContent()}</p>
160
184
  {:else}
@@ -236,6 +260,29 @@
236
260
  font-style: italic;
237
261
  }
238
262
 
263
+ .st-streamingMessage__reasoning {
264
+ background: var(--st-component-chatMessage-reasoningBackground, var(--st-semantic-surface-subtle));
265
+ border-left: 2px solid var(--st-semantic-border-subtle);
266
+ border-radius: var(--st-component-control-radius, 0.375rem);
267
+ margin: 0 0 0.5rem;
268
+ padding: 0.4rem 0.6rem;
269
+ }
270
+
271
+ .st-streamingMessage__reasoningToggle {
272
+ color: var(--st-semantic-text-muted);
273
+ cursor: pointer;
274
+ font-size: 0.75rem;
275
+ font-weight: 600;
276
+ }
277
+
278
+ .st-streamingMessage__reasoningText {
279
+ color: var(--st-semantic-text-secondary);
280
+ font-size: 0.8125rem;
281
+ font-style: italic;
282
+ margin: 0.35rem 0 0;
283
+ white-space: pre-wrap;
284
+ }
285
+
239
286
  .st-streamingMessage__meta {
240
287
  color: var(--st-semantic-text-muted);
241
288
  font-size: 0.75rem;
@@ -8,6 +8,13 @@ export type StreamingMessageEvent = {
8
8
  } | {
9
9
  type: "message.completed";
10
10
  messageId?: string;
11
+ } | {
12
+ type: "reasoning.delta";
13
+ messageId?: string;
14
+ delta: string;
15
+ } | {
16
+ type: "reasoning.completed";
17
+ messageId?: string;
11
18
  } | {
12
19
  type: "tool.started";
13
20
  toolCallId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"StreamingMessage.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StreamingMessage.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAoB,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAClF;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAAC;AAQtD,KAAK,qBAAqB,GAAG,IAAI,CAC/B,cAAc,CAAC,WAAW,CAAC,EAC3B,OAAO,GAAG,UAAU,GAAG,MAAM,CAC9B,GAAG;IACF,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACxC,MAAM,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0KJ,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"StreamingMessage.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StreamingMessage.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAoB,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAClF;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAAC;AAQtD,KAAK,qBAAqB,GAAG,IAAI,CAC/B,cAAc,CAAC,WAAW,CAAC,EAC3B,OAAO,GAAG,UAAU,GAAG,MAAM,CAC9B,GAAG;IACF,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACxC,MAAM,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAgMJ,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -68,8 +68,12 @@
68
68
  transform: translateX(1rem);
69
69
  }
70
70
 
71
+ /* Focus = stratégie d'anatomie partagée (outline DSFR / inset Carbon / ring base). */
71
72
  .st-switch__input:focus-visible + .st-switch__track {
72
- box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
73
+ outline: var(--st-component-control-anatomy-focus-outline, none);
74
+ outline-offset: var(--st-component-control-anatomy-focus-offset, 0);
75
+ box-shadow: var(--st-component-control-anatomy-focus-boxShadow,
76
+ 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive)));
73
77
  }
74
78
 
75
79
  .st-switch__content {
package/dist/Tabs.svelte CHANGED
@@ -63,7 +63,9 @@
63
63
 
64
64
  <style>
65
65
  .st-tabs {
66
- color: var(--st-component-tabs-activeText, var(--st-semantic-text-primary));
66
+ /* Wrapper/panel text = neutral primary (NOT the active-tab colour, which is
67
+ Bleu France in DSFR and must not bleed into the panel body). */
68
+ color: var(--st-semantic-text-primary);
67
69
  display: grid;
68
70
  gap: var(--st-spacing-4, 1rem);
69
71
  }
@@ -77,22 +79,49 @@
77
79
  .st-tabs__tab {
78
80
  background: transparent;
79
81
  border: 0;
80
- border-bottom: 2px solid transparent;
82
+ /* Indicator edge (F7/F8): base/Carbon carry it on the BOTTOM, DSFR on the
83
+ TOP. The resting tab keeps the same-width transparent stroke on each edge
84
+ so the baseline does not shift when a tab becomes active. */
85
+ border-top-width: var(--st-component-tabs-activeBorderTopWidth, 0);
86
+ border-bottom-width: var(--st-component-tabs-activeBorderBottomWidth, var(--st-component-tabs-anatomy-shape-borderWidth, 2px));
87
+ border-top-style: var(--st-component-tabs-anatomy-shape-borderStyle, solid);
88
+ border-bottom-style: var(--st-component-tabs-anatomy-shape-borderStyle, solid);
89
+ border-top-color: transparent;
90
+ border-bottom-color: transparent;
81
91
  color: var(--st-component-tabs-inactiveText, var(--st-semantic-text-secondary));
82
- cursor: pointer;
83
- font: inherit;
84
- font-weight: 600;
85
- padding: 0.75rem 0.25rem;
92
+ cursor: var(--st-cursor-interactive, pointer);
93
+ font-family: var(--st-component-tabs-anatomy-typography-family, inherit);
94
+ font-size: var(--st-component-tabs-tabFontSize, inherit);
95
+ font-weight: var(--st-component-tabs-anatomy-typography-weight, 600);
96
+ line-height: var(--st-component-tabs-tabLineHeight, var(--st-component-tabs-anatomy-typography-lineHeight, 1.2));
97
+ letter-spacing: var(--st-component-tabs-anatomy-typography-letterSpacing, 0);
98
+ text-transform: var(--st-component-tabs-anatomy-typography-textTransform, none);
99
+ padding: var(--st-component-tabs-tabPaddingBlock, 0.75rem) var(--st-component-tabs-tabPaddingInline, 0.25rem);
86
100
  }
87
101
 
88
102
  .st-tabs__tab--active {
103
+ background: var(--st-component-tabs-activeBackground, transparent);
104
+ border-top-color: var(--st-component-tabs-indicator, var(--st-semantic-action-primary));
89
105
  border-bottom-color: var(--st-component-tabs-indicator, var(--st-semantic-action-primary));
106
+ /* Indicator accent when the theme draws it as a box-shadow (DSFR top filet)
107
+ rather than a real border — keeps both border sides at `0 none`. */
108
+ box-shadow: var(--st-component-tabs-activeShadow, none);
109
+ font-weight: var(--st-component-tabs-activeWeight, var(--st-component-tabs-anatomy-typography-weight, 600));
90
110
  color: var(--st-component-tabs-activeText, var(--st-semantic-text-primary));
91
111
  }
92
112
 
113
+ /* a11y (non-negotiable): tabs previously had NO visible focus ring. The
114
+ shared focus mixin restores keyboard visibility per theme. */
115
+ .st-tabs__tab:focus-visible {
116
+ outline: var(--st-component-tabs-anatomy-focus-outline, 2px solid var(--st-semantic-border-interactive));
117
+ outline-offset: var(--st-component-tabs-anatomy-focus-offset, 2px);
118
+ box-shadow: var(--st-component-tabs-anatomy-focus-boxShadow, none);
119
+ border-radius: var(--st-component-tabs-anatomy-shape-radius, 0);
120
+ }
121
+
93
122
  .st-tabs__tab:disabled {
94
- cursor: not-allowed;
95
- opacity: 0.55;
123
+ cursor: var(--st-cursor-disabled, not-allowed);
124
+ opacity: var(--st-component-tabs-anatomy-states-disabled-opacity, 0.55);
96
125
  }
97
126
 
98
127
  .st-tabs__panel {
@@ -55,8 +55,12 @@
55
55
  }
56
56
 
57
57
  .st-field__label {
58
- font-size: 0.875rem;
59
- font-weight: 600;
58
+ font-family: var(--st-component-field-labelTypography-family, inherit);
59
+ font-size: var(--st-component-field-labelTypography-size, 0.875rem);
60
+ font-weight: var(--st-component-field-labelTypography-weight, 600);
61
+ line-height: var(--st-component-field-labelTypography-lineHeight, 1.4);
62
+ letter-spacing: var(--st-component-field-labelTypography-letterSpacing, 0);
63
+ text-transform: var(--st-component-field-labelTypography-textTransform, none);
60
64
  }
61
65
 
62
66
  .st-field__help,
@@ -73,23 +77,57 @@
73
77
  color: var(--st-component-field-errorText, var(--st-semantic-feedback-error));
74
78
  }
75
79
 
80
+ /* Field style (anatomy v1.2.0) — same resolved field anatomy as Input:
81
+ outline (base) = surface fill + 4 equal borders; filled-underline
82
+ (DSFR/Carbon) = filled bg + bottom rule only. Multiline keeps its own
83
+ vertical padding (block padding can't be 0 like a single-line control). */
76
84
  .st-textarea {
77
- background: var(--st-component-control-background, var(--st-semantic-surface-default));
78
- border: 1px solid var(--st-component-control-border, var(--st-semantic-border-subtle));
79
- border-radius: var(--st-component-control-radius, 0.375rem);
85
+ background: var(--st-component-control-anatomy-field-fillBg, var(--st-component-control-background, var(--st-semantic-surface-default)));
86
+ border-top: var(--st-component-control-anatomy-field-borderTop, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
87
+ border-right: var(--st-component-control-anatomy-field-borderRight, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
88
+ border-bottom: var(--st-component-control-anatomy-field-borderBottom, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
89
+ border-left: var(--st-component-control-anatomy-field-borderLeft, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
90
+ /* Per-corner radius + box-shadow underline (anatomy v1.3.0) — same field
91
+ anatomy as Input: TOP corners = radiusTop (DSFR 4px), bottom = shape.radius;
92
+ bottom rule via inset box-shadow. Fallbacks reproduce the prior uniform
93
+ rounded box with no underline shadow → base Sent Tech unchanged. */
94
+ border-top-left-radius: var(--st-component-control-anatomy-field-radiusTop, var(--st-component-control-anatomy-shape-radius, 0.375rem));
95
+ border-top-right-radius: var(--st-component-control-anatomy-field-radiusTop, var(--st-component-control-anatomy-shape-radius, 0.375rem));
96
+ border-bottom-right-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
97
+ border-bottom-left-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
98
+ box-shadow: var(--st-component-control-anatomy-field-underline, none);
80
99
  color: var(--st-component-control-text, var(--st-semantic-text-primary));
81
- font: inherit;
100
+ font-family: var(--st-component-control-anatomy-typography-family, inherit);
101
+ font-size: var(--st-component-control-anatomy-typography-size, inherit);
102
+ font-weight: var(--st-component-control-anatomy-typography-weight, 400);
103
+ line-height: var(--st-component-control-anatomy-typography-lineHeight, 1.5);
104
+ letter-spacing: var(--st-component-control-anatomy-typography-letterSpacing, normal);
82
105
  min-height: 6rem;
83
106
  min-width: 0;
84
- padding: 0.625rem 0.75rem;
107
+ padding: 0.625rem var(--st-component-control-anatomy-density-sm-paddingInline, 0.75rem);
85
108
  resize: vertical;
109
+ transition:
110
+ border-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
111
+ box-shadow var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
86
112
  width: 100%;
87
113
  }
88
114
 
115
+ .st-textarea::placeholder {
116
+ color: var(--st-component-control-placeholderText, var(--st-semantic-text-muted));
117
+ }
118
+
119
+ .st-textarea:hover:not(:disabled) {
120
+ border-color: var(--st-component-control-hoverBorder, var(--st-semantic-border-strong));
121
+ }
122
+
89
123
  .st-textarea:focus-visible {
90
124
  border-color: var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
91
- box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
92
- outline: none;
125
+ outline: var(--st-component-control-anatomy-focus-outline, none);
126
+ outline-offset: var(--st-component-control-anatomy-focus-offset, 0);
127
+ /* Composed field focus box-shadow (v1.3.0): keeps the resting underline. */
128
+ box-shadow: var(--st-component-control-anatomy-field-focusShadow,
129
+ var(--st-component-control-anatomy-focus-boxShadow,
130
+ 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive))));
93
131
  }
94
132
 
95
133
  .st-textarea[aria-invalid="true"] {