@botiverse/raft-ui 0.9.6 → 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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## [0.10.1](https://github.com/botiverse/raft-design/compare/v0.10.0...v0.10.1) (2026-10-20)
2
+
3
+ ### Fixed
4
+
5
+ - A page in Japanese or Korean (`<html lang="ja">` / `"ko"`, including regional tags like `ja-JP`) falls back to its own system faces (Hiragino Sans, Yu Gothic UI, Meiryo, Noto Sans JP; Apple SD Gothic Neo, Malgun Gothic, Noto Sans KR) before the Chinese ones, so kanji keep Japanese glyph shapes and Hangul gets a Korean face. Chinese and default pages are unchanged. ([#603](https://github.com/botiverse/raft-design/pull/603))
6
+
7
+ ## [0.10.0](https://github.com/botiverse/raft-design/compare/v0.9.6...v0.10.0) (2026-10-20)
8
+
9
+ ### Breaking Changes
10
+
11
+ - On a desktop (`(width >= 48rem) and (pointer: fine)`: a wide screen used with a mouse or trackpad) type and controls follow Feishu PC's density. Touch devices keep the phone sizes at any width. Existing names keep working, but their sizes change there, so check any layout that matched them by hand ([#601](https://github.com/botiverse/raft-design/pull/601)):
12
+ - `text-headline` 16/24; `text-body`, `text-subhead` and `text-callout` 14/22; `text-footnote`, `text-caption` and `text-mono-meta` 12/18; `text-mono-code` 13/20. Hero, display, title and label are unchanged.
13
+ - Fields (`Input`, `Select`, `Combobox`, `ComboboxSelect`, `InputGroup`) go from 44px to 32px, with 14/22 field text; card titles are 14/22.
14
+ - `Button` `lg` and `icon-lg` go from 44px to 32px (icon 22 → 18) and keep a 44px hit area; `xl` goes from 52px to 40px. The default `md` stays 32px; its label goes from 13px to 14px.
15
+ - Anything hardcoded to `h-11` / `size-11` to line up with a field no longer matches on desktop: use `h-control` / `size-control` instead.
16
+ - The desktop type values sit after Tailwind's theme output, so they win over an app's own `@theme` values for these names; an app that changes one sets it inside the same query.
17
+ - Field frames (`Input`, `Textarea`, `InputGroup`, `Select`, `Combobox`, `ComboboxSelect`, `Composer`, option `Card`, `SearchTrigger`) are drawn at 1px in `line-field` instead of 1.5px, on the phone too. ([#601](https://github.com/botiverse/raft-design/pull/601))
18
+
19
+ ### Added
20
+
21
+ - Control tokens that switch with the desktop query: `--control-height`, `--control-height-cta`, `--control-icon-size`, `--control-hit-inset`, and the `h-control`, `size-control`, `size-control-icon` and `text-control` utilities (registered with tailwind-merge). ([#601](https://github.com/botiverse/raft-design/pull/601))
22
+
23
+ ### Changed
24
+
25
+ - `Tooltip`, `ComboboxSelect` options, `Steps` and `Timeline` use the type scale instead of fixed sizes, so they follow the desktop values. ([#601](https://github.com/botiverse/raft-design/pull/601))
26
+
1
27
  ## [0.9.6](https://github.com/botiverse/raft-design/compare/v0.9.5...v0.9.6) (2026-10-20)
2
28
 
3
29
  ### Added
package/dist/cn.mjs CHANGED
@@ -1,46 +1,55 @@
1
1
  import { clsx } from "cnfast";
2
2
  import { extendTailwindMerge } from "tailwind-merge";
3
3
  //#region src/lib/cn.ts
4
- const twMerge = extendTailwindMerge({ extend: { classGroups: {
5
- shadow: [{ shadow: [
6
- "float",
7
- "sticker",
8
- "focus"
9
- ] }, { shadow: [{ raft: [
10
- "xs",
11
- "sm",
12
- "md",
13
- "lg",
14
- "xl",
15
- "float",
16
- "sticker",
17
- "focus"
18
- ] }] }],
19
- rounded: [{ rounded: [
20
- "sticker",
21
- "chip",
4
+ const twMerge = extendTailwindMerge({ extend: {
5
+ theme: { spacing: [
22
6
  "control",
23
- "container",
24
- "dialog"
25
- ] }],
26
- "font-size": [{ text: [
27
- "field",
28
- "card-title",
29
- "hero",
30
- "display",
31
- "title",
32
- "headline",
33
- "body",
34
- "subhead",
35
- "callout",
36
- "footnote",
37
- "caption",
38
- "label",
39
- "mono-meta",
40
- "mono-code"
41
- ] }],
42
- "font-weight": [{ font: ["field", "card-title"] }]
43
- } } });
7
+ "control-cta",
8
+ "control-icon",
9
+ "control-hit"
10
+ ] },
11
+ classGroups: {
12
+ shadow: [{ shadow: [
13
+ "float",
14
+ "sticker",
15
+ "focus"
16
+ ] }, { shadow: [{ raft: [
17
+ "xs",
18
+ "sm",
19
+ "md",
20
+ "lg",
21
+ "xl",
22
+ "float",
23
+ "sticker",
24
+ "focus"
25
+ ] }] }],
26
+ rounded: [{ rounded: [
27
+ "sticker",
28
+ "chip",
29
+ "control",
30
+ "container",
31
+ "dialog"
32
+ ] }],
33
+ "font-size": [{ text: [
34
+ "field",
35
+ "card-title",
36
+ "hero",
37
+ "display",
38
+ "title",
39
+ "headline",
40
+ "body",
41
+ "subhead",
42
+ "callout",
43
+ "footnote",
44
+ "caption",
45
+ "label",
46
+ "mono-meta",
47
+ "mono-code",
48
+ "control"
49
+ ] }],
50
+ "font-weight": [{ font: ["field", "card-title"] }]
51
+ }
52
+ } });
44
53
  function cn(...inputs) {
45
54
  return twMerge(clsx(inputs));
46
55
  }
@@ -6,18 +6,18 @@
6
6
  .r-combobox-select__control {
7
7
  box-sizing: border-box;
8
8
  min-width: 0;
9
- min-height: 44px;
9
+ min-height: var(--control-height);
10
10
  display: flex;
11
11
  align-items: center;
12
12
  gap: 0.5rem;
13
13
  padding: 0 6px 0 12px;
14
- border: 1.5px solid var(--line-strong);
14
+ border: 1px solid var(--line-field);
15
15
  border-radius: 8px;
16
16
  outline: 0;
17
17
  background: var(--layer-card);
18
18
  color: var(--foreground);
19
- font-size: 16px;
20
- line-height: 24px;
19
+ font-size: var(--field-font-size);
20
+ line-height: var(--field-line-height);
21
21
  transition: box-shadow 90ms ease-out;
22
22
  }
23
23
  /* The control's ring is a utility on it (combobox-select.tsx): it rings when
@@ -64,8 +64,8 @@
64
64
  border-radius: 6px;
65
65
  background: var(--fill-muted);
66
66
  color: var(--foreground);
67
- font-size: 13px;
68
- line-height: 18px;
67
+ font-size: var(--text-footnote);
68
+ line-height: var(--text-footnote--line-height);
69
69
  font-weight: 600;
70
70
  }
71
71
  .r-combobox-select__control:not(:focus-within) .r-combobox-select__chip[data-overflow] {
@@ -172,8 +172,8 @@
172
172
  cursor: default;
173
173
  user-select: none;
174
174
  color: var(--foreground);
175
- font-size: 14px;
176
- line-height: 20px;
175
+ font-size: var(--text-callout);
176
+ line-height: var(--text-callout--line-height);
177
177
  font-weight: 500;
178
178
  outline: 0;
179
179
  transition:
@@ -215,22 +215,22 @@
215
215
  .r-combobox-select__group-label {
216
216
  padding: 8px 10px 4px;
217
217
  color: var(--foreground-hint);
218
- font-size: 12px;
219
- line-height: 16px;
218
+ font-size: var(--text-caption);
219
+ line-height: var(--text-caption--line-height);
220
220
  font-weight: 700;
221
221
  }
222
222
  .r-combobox-select__message {
223
223
  padding: 14px 16px;
224
224
  color: var(--foreground-hint);
225
- font-size: 14px;
226
- line-height: 20px;
225
+ font-size: var(--text-callout);
226
+ line-height: var(--text-callout--line-height);
227
227
  text-align: start;
228
228
  }
229
229
  .r-combobox-select__overflow {
230
230
  color: var(--foreground-hint);
231
231
  font-family: var(--mono-font, ui-monospace, monospace);
232
- font-size: 12px;
233
- line-height: 16px;
232
+ font-size: var(--text-caption);
233
+ line-height: var(--text-caption--line-height);
234
234
  }
235
235
  .r-combobox-select__spinner {
236
236
  width: 0.875rem;
@@ -239,8 +239,8 @@
239
239
  }
240
240
  .r-combobox-select--sm.r-combobox-select__control {
241
241
  min-height: 1.75rem;
242
- font-size: 13px;
243
- line-height: 18px;
242
+ font-size: var(--text-control);
243
+ line-height: var(--text-control--line-height);
244
244
  }
245
245
  .r-combobox-select--sm .r-combobox-select__chips {
246
246
  min-height: 1.5rem;
@@ -20,9 +20,10 @@
20
20
  --r-steps-indicator-size: 20px;
21
21
  --r-steps-indicator-font-size: 12px;
22
22
  --r-steps-icon-size: 14px;
23
- --r-steps-title-font-size: 13px;
24
- --r-steps-description-font-size: 12px;
25
- --r-steps-line-height: calc(18 / 13);
23
+ /* The type scale's footnote and caption, so a desktop gets its sizes too. */
24
+ --r-steps-title-font-size: var(--text-footnote);
25
+ --r-steps-description-font-size: var(--text-caption);
26
+ --r-steps-line-height: var(--text-footnote--line-height);
26
27
  --r-steps-gap: 8px;
27
28
  --r-steps-connector-gap: 8px;
28
29
  --r-steps-connector-min-width: 16px;
@@ -180,10 +181,7 @@
180
181
  /* The title's first line is centered on the marker. */
181
182
  .r-step__content {
182
183
  min-width: 0;
183
- padding-top: calc(
184
- (var(--r-steps-indicator-size) - var(--r-steps-title-font-size) * var(--r-steps-line-height)) /
185
- 2
186
- );
184
+ padding-top: calc((var(--r-steps-indicator-size) - var(--r-steps-line-height)) / 2);
187
185
  }
188
186
 
189
187
  .r-step__title {
@@ -199,6 +197,7 @@
199
197
  .r-step__description {
200
198
  color: var(--r-step-description-color);
201
199
  font-size: var(--r-steps-description-font-size);
200
+ line-height: var(--text-caption--line-height);
202
201
  margin-top: 2px;
203
202
  }
204
203
 
@@ -14,10 +14,11 @@
14
14
 
15
15
  .r-timeline {
16
16
  --r-timeline-icon-size: var(--r-timeline-custom-icon-size, 9px);
17
- --r-timeline-title-font-size: 14px;
18
- --r-timeline-content-font-size: 12px;
19
- --r-timeline-time-font-size: 12px;
20
- --r-timeline-line-height: 20px;
17
+ /* The type scale's callout and mono meta, so a desktop gets its sizes too. */
18
+ --r-timeline-title-font-size: var(--text-callout);
19
+ --r-timeline-content-font-size: var(--text-mono-meta);
20
+ --r-timeline-time-font-size: var(--text-mono-meta);
21
+ --r-timeline-line-height: var(--text-callout--line-height);
21
22
  --r-timeline-item-inner-gap: 2px;
22
23
  --r-timeline-title-font-family: var(--font-sans);
23
24
  --r-timeline-title-font-weight: 400;
@@ -32,7 +33,7 @@
32
33
  --r-timeline-marker-fill: 100%;
33
34
  --r-timeline-marker-border-color: var(--sticker-ink);
34
35
  /* How far each marker sits below the item's top, to center on the title's first line. */
35
- --r-timeline-axis-offset: calc((20px - var(--r-timeline-icon-size)) / 2);
36
+ --r-timeline-axis-offset: calc((var(--r-timeline-line-height) - var(--r-timeline-icon-size)) / 2);
36
37
 
37
38
  &.r-timeline--lg {
38
39
  --r-timeline-title-font-size: 16px;
@@ -189,7 +190,7 @@
189
190
  color: var(--foreground-hint);
190
191
  font-family: var(--font-mono);
191
192
  font-size: var(--r-timeline-content-font-size);
192
- line-height: 16px;
193
+ line-height: var(--text-mono-meta--line-height);
193
194
  overflow-wrap: anywhere;
194
195
  }
195
196
 
@@ -211,7 +211,7 @@ function Spinner({ size = "sm", variant = "default", className, render, ...props
211
211
  const sticker = (fill) => [`border-sticker-ink ${fill} text-sticker-ink ring-[1.5px] ring-sticker-edge`, "hover:press-overlay-sticker active:press-overlay-sticker"];
212
212
  const outline = (ink) => [`border-line-strong bg-layer-card ${ink}`, "hover:press-overlay active:press-overlay"];
213
213
  const textMd = [
214
- "h-8 gap-2 rounded-control px-3 py-0 text-footnote",
214
+ "h-8 gap-2 rounded-control px-3 py-0 text-control",
215
215
  "has-data-[icon=inline-start]:pl-2.5 has-data-[icon=inline-end]:pr-2.5",
216
216
  "[&_svg:not([class*='size-'])]:size-4 **:data-icon:size-[1em]",
217
217
  "after:absolute after:inset-x-0 after:-inset-y-1.5"
@@ -266,15 +266,16 @@ const buttonVariants = tv$1({
266
266
  sm: { root: textMd },
267
267
  md: { root: textMd },
268
268
  lg: { root: [
269
- "h-11 gap-2 rounded-control px-4 py-0 text-subhead",
269
+ "h-control gap-2 rounded-control px-4 py-0 text-subhead",
270
270
  "has-data-[icon=inline-start]:pl-3.5 has-data-[icon=inline-end]:pr-3.5",
271
- "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]"
271
+ "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]",
272
+ "after:absolute after:inset-x-0 after:-inset-y-control-hit"
272
273
  ] },
273
- xl: { root: ["h-13 gap-2 rounded-control px-5 py-0 text-body", "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]"] },
274
+ xl: { root: ["h-control-cta gap-2 rounded-control px-5 py-0 text-body", "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]"] },
274
275
  "icon-xs": { root: iconMd },
275
276
  "icon-sm": { root: iconMd },
276
277
  "icon-md": { root: iconMd },
277
- "icon-lg": { root: "size-11 rounded-control p-0 [&_svg:not([class*='size-'])]:size-[22px]" }
278
+ "icon-lg": { root: ["size-control rounded-control p-0 [&_svg:not([class*='size-'])]:size-control-icon", "after:absolute after:-inset-control-hit"] }
278
279
  }
279
280
  },
280
281
  defaultVariants: {
package/dist/fonts.css CHANGED
@@ -1,7 +1,9 @@
1
1
  @import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&family=Space+Mono:wght@400;700&display=swap");
2
2
 
3
3
  /* Raft Design System: Space Grotesk is what people say, Space Mono is what
4
- machines write. CJK falls back to PingFang / Source Han Sans. */
4
+ machines write. CJK falls back to PingFang / Source Han Sans; a Japanese or
5
+ Korean page (<html lang="ja|ko">) falls back to its own faces first, so kanji
6
+ keep Japanese glyph shapes and Hangul a Korean face. */
5
7
  :where(:root) {
6
8
  --heading-font:
7
9
  "Space Grotesk", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC",
@@ -11,3 +13,21 @@
11
13
  system-ui, sans-serif;
12
14
  --mono-font: "Space Mono", ui-monospace, "SF Mono", Menlo, monospace;
13
15
  }
16
+
17
+ :where(:root:lang(ja)) {
18
+ --heading-font:
19
+ "Space Grotesk", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", Meiryo,
20
+ "Noto Sans JP", "Noto Sans CJK JP", system-ui, sans-serif;
21
+ --sans-font:
22
+ "Space Grotesk", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", Meiryo,
23
+ "Noto Sans JP", "Noto Sans CJK JP", system-ui, sans-serif;
24
+ }
25
+
26
+ :where(:root:lang(ko)) {
27
+ --heading-font:
28
+ "Space Grotesk", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", "Noto Sans CJK KR",
29
+ system-ui, sans-serif;
30
+ --sans-font:
31
+ "Space Grotesk", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", "Noto Sans CJK KR",
32
+ system-ui, sans-serif;
33
+ }
@@ -275,6 +275,17 @@
275
275
  --card-title-font-size: 16px;
276
276
  --card-title-font-weight: 600;
277
277
  --card-title-line-height: 24px;
278
+ /* A default control: a field, a Button at lg, an icon button at icon-lg. A
279
+ finger's 44px on a phone, Feishu's 32px on a desktop. */
280
+ --control-height: 44px;
281
+ /* The full-width call to action across a sign-in, sign-up or setup form
282
+ (Button xl). */
283
+ --control-height-cta: 52px;
284
+ /* The icon in a control-height icon button. */
285
+ --control-icon-size: 22px;
286
+ /* How far a control-height button's hit area reaches past its top and bottom,
287
+ to keep 44px. */
288
+ --control-hit-inset: 0px;
278
289
  --sticker-ink: oklch(0.1813 0.005 17.62);
279
290
  --sticker-paper: oklch(1 0 0);
280
291
  --sticker-edge: oklch(0.1813 0.005 17.62 / 0);
@@ -628,3 +639,21 @@
628
639
  --secondary-900: var(--palette-stone-900);
629
640
  --secondary-950: var(--palette-stone-950);
630
641
  }
642
+
643
+ /* Desktop: from 768px (Tailwind's `md`, where the desktop layout begins)
644
+ with a mouse or trackpad. The metrics that follow Feishu's PC specs. A
645
+ phone, and a touch screen at any width, keep the values above. The same
646
+ query as the desktop type in styles.css. */
647
+ @media (width >= 48rem) and (pointer: fine) {
648
+ :where(:root),
649
+ .light {
650
+ --field-font-size: 14px;
651
+ --field-line-height: 22px;
652
+ --card-title-font-size: 14px;
653
+ --card-title-line-height: 22px;
654
+ --control-height: 32px;
655
+ --control-height-cta: 40px;
656
+ --control-icon-size: 18px;
657
+ --control-hit-inset: 6px;
658
+ }
659
+ }
@@ -89,7 +89,7 @@ declare const buttonVariants: import("tailwind-variants/lite").TVReturnType<{
89
89
  root: string[];
90
90
  };
91
91
  "icon-lg": {
92
- root: string;
92
+ root: string[];
93
93
  };
94
94
  };
95
95
  }, {
@@ -164,7 +164,7 @@ declare const buttonVariants: import("tailwind-variants/lite").TVReturnType<{
164
164
  root: string[];
165
165
  };
166
166
  "icon-lg": {
167
- root: string;
167
+ root: string[];
168
168
  };
169
169
  };
170
170
  }, {
@@ -239,7 +239,7 @@ declare const buttonVariants: import("tailwind-variants/lite").TVReturnType<{
239
239
  root: string[];
240
240
  };
241
241
  "icon-lg": {
242
- root: string;
242
+ root: string[];
243
243
  };
244
244
  };
245
245
  }, {
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as VariantProps, C as DialogTitle, D as Button, E as DialogTriggerProps, M as StyledProps, O as ButtonProps, S as DialogSurfaceProps, T as DialogTrigger, _ as DialogOverlayProps, a as DialogCloseProps, b as DialogProps, c as DialogDescription, d as DialogFooterProps, f as DialogHeader, g as DialogOverlay, h as DialogNavProps, i as DialogClose, j as DataAttributes$1, k as buttonVariants, l as DialogDescriptionProps, m as DialogNav, n as DialogBody, o as DialogContent, p as DialogHeaderProps, r as DialogBodyProps, s as DialogContentProps, t as Dialog, u as DialogFooter, v as DialogPortal, w as DialogTitleProps, x as DialogSurface, y as DialogPortalProps } from "./index-DG62I-YM.mjs";
1
+ import { A as VariantProps, C as DialogTitle, D as Button, E as DialogTriggerProps, M as StyledProps, O as ButtonProps, S as DialogSurfaceProps, T as DialogTrigger, _ as DialogOverlayProps, a as DialogCloseProps, b as DialogProps, c as DialogDescription, d as DialogFooterProps, f as DialogHeader, g as DialogOverlay, h as DialogNavProps, i as DialogClose, j as DataAttributes$1, k as buttonVariants, l as DialogDescriptionProps, m as DialogNav, n as DialogBody, o as DialogContent, p as DialogHeaderProps, r as DialogBodyProps, s as DialogContentProps, t as Dialog, u as DialogFooter, v as DialogPortal, w as DialogTitleProps, x as DialogSurface, y as DialogPortalProps } from "./index-CDHLWJtB.mjs";
2
2
  import { cn } from "./cn.mjs";
3
3
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
4
4
  import { CSSProperties, ComponentProps, ComponentPropsWithRef, ComponentPropsWithoutRef, CompositionEventHandler, FocusEventHandler, FormEventHandler, HTMLAttributes, KeyboardEventHandler, PointerEvent, ReactNode, Ref, RefObject, SVGProps } from "react";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cn } from "./cn.mjs";
2
- import { C as ThemeStateContext, S as useTheme, _ as defaultNativeButton, a as DialogDescription, b as resolveThemeScopeProps, c as DialogNav, d as DialogSurface, f as DialogTitle, g as Spinner, h as buttonVariants, i as DialogContent, l as DialogOverlay, m as Button, n as DialogBody, o as DialogFooter, p as DialogTrigger, r as DialogClose, s as DialogHeader, t as Dialog, u as DialogPortal, v as useParentOverlayKind, w as tv, x as useThemeScopeProps, y as withOverlayLayer } from "./dialog-BYH5ojYr.mjs";
2
+ import { C as ThemeStateContext, S as useTheme, _ as defaultNativeButton, a as DialogDescription, b as resolveThemeScopeProps, c as DialogNav, d as DialogSurface, f as DialogTitle, g as Spinner, h as buttonVariants, i as DialogContent, l as DialogOverlay, m as Button, n as DialogBody, o as DialogFooter, p as DialogTrigger, r as DialogClose, s as DialogHeader, t as Dialog, u as DialogPortal, v as useParentOverlayKind, w as tv, x as useThemeScopeProps, y as withOverlayLayer } from "./dialog-CDPAT3DH.mjs";
3
3
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
4
4
  import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Copy, FileText, Image, Info, Minus, Upload, X, Zap } from "lucide-react";
5
5
  import { Children, Fragment, cloneElement, createContext, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -889,7 +889,7 @@ const tooltip = tv({
889
889
  "has-[[data-slot=kbd]]:py-1 has-[[data-slot=kbd]]:pr-2.5"
890
890
  ],
891
891
  body: [
892
- "block min-w-0 text-[13px] leading-4.5 whitespace-normal wrap-break-word",
892
+ "block min-w-0 text-footnote whitespace-normal wrap-break-word",
893
893
  "**:data-[slot=kbd]:relative **:data-[slot=kbd]:z-10 [&_[data-slot=kbd]]:ml-1.5",
894
894
  "[&_[data-slot=kbd]]:h-4 [&_[data-slot=kbd]]:min-w-4 [&_[data-slot=kbd]]:px-1 [&_[data-slot=kbd]]:py-0",
895
895
  "**:data-[slot=kbd]:text-[12px] **:data-[slot=kbd]:leading-4 [&_[data-slot=kbd]]:font-normal"
@@ -1241,7 +1241,7 @@ const card = tv({
1241
1241
  * in the control plane, and its title drops from the card-title scale
1242
1242
  * to the field scale so it cannot outrank the field label above it. */
1243
1243
  option: {
1244
- root: ["rounded-control border-[1.5px] border-line-field bg-layer-card", "shadow-none"],
1244
+ root: ["rounded-control border border-line-field bg-layer-card", "shadow-none"],
1245
1245
  header: ["gap-x-3 p-3"],
1246
1246
  content: ["px-3 pb-3"],
1247
1247
  title: ["font-sans text-callout font-semibold"],
@@ -4630,8 +4630,8 @@ const Label = Object.assign(LabelRoot, {
4630
4630
  //#endregion
4631
4631
  //#region src/components/input/input.recipe.ts
4632
4632
  const input = tv({ slots: { root: [
4633
- "h-11 w-full px-3 py-0 font-heading text-field font-field outline-none",
4634
- "rounded-control border-[1.5px] border-line-strong bg-layer-card",
4633
+ "h-control w-full px-3 py-0 font-heading text-field font-field outline-none",
4634
+ "rounded-control border border-line-field bg-layer-card",
4635
4635
  "text-foreground placeholder:text-foreground-hint",
4636
4636
  "shadow-none transition-shadow duration-90 ease-out focus-visible:shadow-focus",
4637
4637
  "data-invalid:border-danger-strong",
@@ -4746,7 +4746,7 @@ const inputGroup = tv({
4746
4746
  root: [
4747
4747
  "group/input-group relative flex w-full min-w-0 items-center",
4748
4748
  "has-data-[input-group-trim=inline-end]:[--ig-control-pr:0]",
4749
- "h-11 overflow-hidden rounded-control border-[1.5px] border-line-strong bg-layer-card text-foreground",
4749
+ "h-control overflow-hidden rounded-control border border-line-field bg-layer-card text-foreground",
4750
4750
  "shadow-none transition-shadow duration-90 ease-out has-focus-visible:shadow-focus",
4751
4751
  "has-[[data-slot=input-group-control][data-invalid]]:border-danger-strong",
4752
4752
  "has-[[data-slot=input-group-control]:disabled]:cursor-not-allowed has-[[data-slot=input-group-control]:disabled]:border-line-hairline has-[[data-slot=input-group-control]:disabled]:bg-fill-muted",
@@ -4909,7 +4909,7 @@ const combobox = tv({
4909
4909
  "[&>span[data-slot='combobox-trigger-content']]:min-w-0 [&>span[data-slot='combobox-trigger-content']]:flex-1",
4910
4910
  "[&>span[data-slot='combobox-trigger-content']>svg]:shrink-0",
4911
4911
  "h-8 gap-1.5 rounded-control border-transparent bg-transparent px-2.5",
4912
- "text-footnote font-semibold text-foreground-muted",
4912
+ "text-control font-semibold text-foreground-muted",
4913
4913
  "inset-ring inset-ring-line-seam hover:bg-transparent",
4914
4914
  "data-has-value:bg-info-soft data-has-value:text-foreground data-has-value:inset-ring-0 data-has-value:hover:bg-info-soft",
4915
4915
  "focus-visible:shadow-focus",
@@ -4933,7 +4933,7 @@ const combobox = tv({
4933
4933
  "[&_[data-slot='combobox-clear']]:size-5 [&_[data-slot='combobox-clear']]:min-w-5 **:data-[slot='combobox-clear']:shrink-0",
4934
4934
  "[&_[data-slot='combobox-trigger-indicator']]:size-4",
4935
4935
  "[&_[data-slot='combobox-trigger-indicator']>svg]:shrink-0",
4936
- "min-h-11 rounded-control border-[1.5px] border-line-strong bg-layer-card px-3 py-0",
4936
+ "min-h-control rounded-control border border-line-field bg-layer-card px-3 py-0",
4937
4937
  "font-sans text-body text-foreground",
4938
4938
  "transition-shadow duration-90 ease-out has-focus-visible:shadow-focus",
4939
4939
  "[&>svg]:text-foreground-hint [&>svg:not([class*='size-'])]:size-4.5",
@@ -4977,7 +4977,7 @@ const combobox = tv({
4977
4977
  "pointer-events-none opacity-0 outline-none transition-[background-color,color] duration-100",
4978
4978
  "data-visible:pointer-events-auto data-visible:opacity-100",
4979
4979
  "h-7 min-w-0 rounded-control px-2",
4980
- "font-sans text-footnote font-semibold text-foreground-muted",
4980
+ "font-sans text-control font-semibold text-foreground-muted",
4981
4981
  "hover:bg-fill-pressed hover:text-foreground active:bg-fill-pressed",
4982
4982
  "focus-visible:shadow-focus",
4983
4983
  "[&>svg]:size-4 [&>svg]:stroke-2 [&>svg_*]:stroke-2"
@@ -4986,7 +4986,7 @@ const combobox = tv({
4986
4986
  groupLabel: ["py-2", "px-2.5 pt-2 pb-1 text-caption font-bold text-foreground-hint"],
4987
4987
  chips: [
4988
4988
  "flex flex-wrap items-center",
4989
- "min-h-11 gap-1.5 rounded-control border-[1.5px] border-line-strong bg-layer-card py-1.5 pr-3 pl-1.5",
4989
+ "min-h-control gap-1.5 rounded-control border border-line-field bg-layer-card py-1.5 pr-3 pl-1.5",
4990
4990
  "transition-shadow duration-90 ease-out has-focus-visible:shadow-focus"
4991
4991
  ],
4992
4992
  chip: [
@@ -5326,7 +5326,7 @@ const pickerTriggerButton = tv({ base: [
5326
5326
  "has-data-[icon=inline-start]:pl-2.5 has-data-[icon=inline-end]:pr-2.5",
5327
5327
  "[&_svg]:pointer-events-none [&_svg]:shrink-0 **:data-icon:size-[1em]",
5328
5328
  "gap-1.5 rounded-control border-0 bg-transparent px-2.5 py-0",
5329
- "font-sans text-footnote font-semibold text-foreground-muted",
5329
+ "font-sans text-control font-semibold text-foreground-muted",
5330
5330
  "inset-ring inset-ring-line-seam",
5331
5331
  "hover:bg-fill-pressed active:bg-fill-pressed",
5332
5332
  "data-has-value:bg-info-soft data-has-value:text-foreground data-has-value:inset-ring-0",
@@ -5358,7 +5358,8 @@ const select = tv({
5358
5358
  "**:data-[slot='button-content']:w-full **:data-[slot='button-content']:justify-between",
5359
5359
  "[&>span[data-slot='select-trigger-content']]:min-w-0 [&>span[data-slot='select-trigger-content']]:flex-1",
5360
5360
  "[&_[data-slot='select-trigger-content']_svg]:shrink-0",
5361
- "h-11 w-full gap-2 pr-3 pl-3.5",
5361
+ "h-control w-full gap-2 pr-3 pl-3.5",
5362
+ "border border-line-field",
5362
5363
  "text-subhead font-semibold text-foreground",
5363
5364
  "data-placeholder:font-medium data-placeholder:text-foreground-hint",
5364
5365
  "data-invalid:border-danger-strong data-invalid:hover:border-danger-strong",
@@ -5427,7 +5428,7 @@ const select = tv({
5427
5428
  trigger: [
5428
5429
  "px-3 text-field font-field",
5429
5430
  "**:data-[slot='button-content']:font-field **:data-[slot='select-value']:font-field",
5430
- "h-11 py-0",
5431
+ "h-control py-0",
5431
5432
  "data-placeholder:font-field"
5432
5433
  ],
5433
5434
  item: ["text-field font-field", "**:data-[slot='select-item-text']:font-field"]
@@ -5943,11 +5944,11 @@ function ToggleGroupLabel({ className, ...props }) {
5943
5944
  //#endregion
5944
5945
  //#region src/components/textarea/textarea.tsx
5945
5946
  const textarea = tv({ slots: { root: [
5946
- "block w-full min-h-24 resize-y pl-3 pr-(--ta-pr,0.75rem)",
5947
+ "block w-full min-h-[calc(var(--field-line-height)*3+--spacing(6))] resize-y pl-3 pr-(--ta-pr,0.75rem)",
5947
5948
  "scroll-pb-(--ta-scroll-pb,0)",
5948
5949
  "font-heading text-field font-field outline-none",
5949
5950
  "py-2.5",
5950
- "rounded-control border-[1.5px] border-line-strong bg-layer-card",
5951
+ "rounded-control border border-line-field bg-layer-card",
5951
5952
  "text-foreground placeholder:text-foreground-hint",
5952
5953
  "shadow-none transition-shadow duration-90 ease-out focus-visible:shadow-focus",
5953
5954
  "data-invalid:border-danger-strong",
@@ -6778,7 +6779,8 @@ const messageItem = tv({
6778
6779
  ],
6779
6780
  continuationTime: [
6780
6781
  "absolute right-0 select-none whitespace-nowrap tabular-nums",
6781
- "top-1 font-mono text-mono-meta",
6782
+ "top-[calc((var(--text-body--line-height)-var(--text-mono-meta--line-height))/2)]",
6783
+ "font-mono text-mono-meta",
6782
6784
  "text-(--mi-ct-color,transparent)"
6783
6785
  ],
6784
6786
  content: ["min-w-0 flex-1", "text-body text-foreground"],
@@ -7524,7 +7526,7 @@ function MessageMultiSelectCheckbox({ className, ...props }) {
7524
7526
  const messageTextSelectionToolbar = tv({ slots: {
7525
7527
  toolbar: [
7526
7528
  "flex min-w-0 items-center select-none",
7527
- "gap-0.5 overflow-visible rounded-container p-0.5 text-footnote font-bold",
7529
+ "gap-0.5 overflow-visible rounded-container p-0.5 text-control font-bold",
7528
7530
  "bg-layer-popover ring-1 ring-line-seam ring-inset shadow-float"
7529
7531
  ],
7530
7532
  button: [
@@ -8120,9 +8122,9 @@ const messageActionCard = tv({
8120
8122
  "size-[27px] rounded-sticker bg-fill-muted text-foreground-muted",
8121
8123
  "[&>svg]:size-3.5"
8122
8124
  ],
8123
- headerTitle: ["min-w-0 flex-1", "pt-[3.5px]"],
8125
+ headerTitle: ["min-w-0 flex-1", "pt-[calc((27px-1lh)/2)]"],
8124
8126
  headerDescription: ["block", "mt-0.5 text-footnote font-normal text-foreground-muted"],
8125
- headerStatus: ["ml-auto shrink-0", "mt-[5.5px] text-caption font-semibold text-foreground-muted"],
8127
+ headerStatus: ["ml-auto shrink-0", "mt-[calc((27px-1lh)/2)] text-caption font-semibold text-foreground-muted"],
8126
8128
  status: [
8127
8129
  "inline-flex h-auto items-center gap-1.5 rounded-none border-0 bg-transparent p-0 shadow-none",
8128
8130
  "text-caption font-semibold text-foreground-muted",
@@ -10912,15 +10914,15 @@ function FileAction({ variant, size = "icon-sm", ...props }) {
10912
10914
  const composer = tv({ slots: {
10913
10915
  root: [
10914
10916
  "flex w-full flex-col gap-2 p-2",
10915
- "rounded-container border-[1.5px] px-3 pt-3 pb-2",
10916
- "border-line-strong bg-layer-card text-foreground shadow-none",
10917
+ "rounded-container border px-3 pt-3 pb-2",
10918
+ "border-line-field bg-layer-card text-foreground shadow-none",
10917
10919
  "has-focus-visible:shadow-focus"
10918
10920
  ],
10919
10921
  input: [
10920
10922
  "max-h-32 w-full resize-none bg-transparent outline-none",
10921
10923
  "field-sizing-content overflow-y-auto",
10922
10924
  "min-h-6 px-0.5",
10923
- "font-sans text-body font-normal text-foreground md:text-body",
10925
+ "font-sans text-body font-normal text-foreground",
10924
10926
  "placeholder:text-foreground-hint"
10925
10927
  ],
10926
10928
  toolbar: ["flex items-center justify-between", "gap-2"],
@@ -12036,7 +12038,8 @@ const banner = tv({
12036
12038
  "has-[>svg]:grid-cols-[20px_minmax(0,1fr)]",
12037
12039
  "has-[>[data-slot=status]]:grid-cols-[20px_minmax(0,1fr)]",
12038
12040
  "[&>svg]:row-start-1 [&>svg]:size-5 [&>svg]:text-foreground",
12039
- "*:data-[slot=status]:mt-[5.5px] *:data-[slot=status]:self-start *:data-[slot=status]:justify-self-center",
12041
+ "[&>svg]:mt-[calc((1lh-20px)/2)]",
12042
+ "*:data-[slot=status]:mt-[calc((var(--text-callout--line-height)-9px)/2)] *:data-[slot=status]:self-start *:data-[slot=status]:justify-self-center",
12040
12043
  "has-[>svg]:[--banner-text-col:2]",
12041
12044
  "has-[>[data-slot=status]]:[--banner-text-col:2]",
12042
12045
  "has-[>[data-slot=banner-title]]:[--banner-desc-row:2]"
@@ -12070,7 +12073,8 @@ const banner = tv({
12070
12073
  sm: { root: [
12071
12074
  "gap-x-2 px-3 py-2 text-footnote",
12072
12075
  "has-[>svg]:grid-cols-[16px_minmax(0,1fr)] has-[>[data-slot=status]]:grid-cols-[16px_minmax(0,1fr)]",
12073
- "[&>svg]:mt-px [&>svg]:size-4 *:data-[slot=status]:mt-[4.5px]"
12076
+ "[&>svg]:mt-[calc((1lh-16px)/2)] [&>svg]:size-4",
12077
+ "*:data-[slot=status]:mt-[calc((var(--text-footnote--line-height)-9px)/2)]"
12074
12078
  ] },
12075
12079
  md: { root: ["px-3.5 py-3"] },
12076
12080
  lg: { root: ["px-4 py-3.5"] }
@@ -13496,7 +13500,7 @@ const panelAction = tv({
13496
13500
  base: [
13497
13501
  "whitespace-nowrap",
13498
13502
  "disabled:pointer-events-none disabled:transform-none",
13499
- "gap-1.5 text-footnote font-bold",
13503
+ "gap-1.5 text-control font-bold",
13500
13504
  "disabled:bg-transparent disabled:text-foreground-disabled disabled:opacity-100",
13501
13505
  "aria-pressed:border-sticker-ink aria-pressed:bg-brand aria-pressed:text-sticker-ink aria-pressed:ring-[1.5px] aria-pressed:ring-sticker-edge",
13502
13506
  "aria-pressed:active:press-overlay-sticker"
@@ -13506,7 +13510,7 @@ const panelToggleAction = tv({
13506
13510
  extend: panelActionBase,
13507
13511
  base: [
13508
13512
  "data-disabled:pointer-events-none data-disabled:transform-none",
13509
- "gap-1.5 text-footnote font-bold",
13513
+ "gap-1.5 text-control font-bold",
13510
13514
  "data-disabled:bg-transparent data-disabled:text-foreground-disabled data-disabled:opacity-100",
13511
13515
  "data-pressed:border-sticker-ink data-pressed:bg-brand data-pressed:text-sticker-ink data-pressed:ring-[1.5px] data-pressed:ring-sticker-edge",
13512
13516
  "data-pressed:active:press-overlay-sticker"
@@ -15982,7 +15986,7 @@ const inboxItem = tv({
15982
15986
  ],
15983
15987
  itemTitle: [
15984
15988
  "relative line-clamp-2 min-w-0 flex-1 pl-[18px]",
15985
- "[&>svg]:absolute [&>svg]:top-[3px] [&>svg]:left-0 [&>svg]:size-3.5",
15989
+ "[&>svg]:absolute [&>svg]:top-[calc((1lh-14px)/2)] [&>svg]:left-0 [&>svg]:size-3.5",
15986
15990
  "font-sans text-subhead font-semibold text-foreground-muted",
15987
15991
  "group-data-[unread=true]/inbox-item:font-bold group-data-[unread=true]/inbox-item:text-foreground",
15988
15992
  "[&>svg]:text-foreground-hint"
@@ -16802,7 +16806,7 @@ function SearchThreadResultMessages({ className, render, ...props }) {
16802
16806
  const searchTrigger = tv({ slots: {
16803
16807
  root: [
16804
16808
  "relative inline-flex h-9 w-[clamp(360px,32vw,520px)] max-w-full min-w-0 shrink-0 items-center gap-2 px-3",
16805
- "rounded-control border-[1.5px] border-line-strong bg-layer-card",
16809
+ "rounded-control border border-line-field bg-layer-card",
16806
16810
  "text-left font-sans text-body font-normal text-foreground-hint",
16807
16811
  "select-none outline-none transition-colors duration-100",
16808
16812
  "hover:press-overlay active:press-overlay focus-visible:shadow-focus",
package/dist/style.css CHANGED
@@ -269,10 +269,10 @@
269
269
  }
270
270
  .r-timeline {
271
271
  --r-timeline-icon-size: var(--r-timeline-custom-icon-size, 9px);
272
- --r-timeline-title-font-size: 14px;
273
- --r-timeline-content-font-size: 12px;
274
- --r-timeline-time-font-size: 12px;
275
- --r-timeline-line-height: 20px;
272
+ --r-timeline-title-font-size: var(--text-callout);
273
+ --r-timeline-content-font-size: var(--text-mono-meta);
274
+ --r-timeline-time-font-size: var(--text-mono-meta);
275
+ --r-timeline-line-height: var(--text-callout--line-height);
276
276
  --r-timeline-item-inner-gap: 2px;
277
277
  --r-timeline-title-font-family: var(--font-sans);
278
278
  --r-timeline-title-font-weight: 400;
@@ -285,7 +285,7 @@
285
285
  --r-timeline-marker-radius: 2px;
286
286
  --r-timeline-marker-fill: 100%;
287
287
  --r-timeline-marker-border-color: var(--sticker-ink);
288
- --r-timeline-axis-offset: calc((20px - var(--r-timeline-icon-size)) / 2);
288
+ --r-timeline-axis-offset: calc((var(--r-timeline-line-height) - var(--r-timeline-icon-size)) / 2);
289
289
 
290
290
  &.r-timeline--lg {
291
291
  --r-timeline-title-font-size: 16px;
@@ -430,8 +430,8 @@
430
430
  color: var(--foreground-hint);
431
431
  font-family: var(--font-mono);
432
432
  font-size: var(--r-timeline-content-font-size);
433
+ line-height: var(--text-mono-meta--line-height);
433
434
  overflow-wrap: anywhere;
434
- line-height: 16px;
435
435
  }
436
436
 
437
437
  .r-timeline-time {
@@ -501,9 +501,9 @@
501
501
  --r-steps-indicator-size: 20px;
502
502
  --r-steps-indicator-font-size: 12px;
503
503
  --r-steps-icon-size: 14px;
504
- --r-steps-title-font-size: 13px;
505
- --r-steps-description-font-size: 12px;
506
- --r-steps-line-height: calc(18 / 13);
504
+ --r-steps-title-font-size: var(--text-footnote);
505
+ --r-steps-description-font-size: var(--text-caption);
506
+ --r-steps-line-height: var(--text-footnote--line-height);
507
507
  --r-steps-gap: 8px;
508
508
  --r-steps-connector-gap: 8px;
509
509
  --r-steps-connector-min-width: 16px;
@@ -655,8 +655,7 @@
655
655
 
656
656
  .r-step__content {
657
657
  min-width: 0;
658
- padding-top: calc((var(--r-steps-indicator-size) - var(--r-steps-title-font-size) * var(--r-steps-line-height)) /
659
- 2);
658
+ padding-top: calc((var(--r-steps-indicator-size) - var(--r-steps-line-height)) / 2);
660
659
  }
661
660
 
662
661
  .r-step__title {
@@ -672,6 +671,7 @@
672
671
  .r-step__description {
673
672
  color: var(--r-step-description-color);
674
673
  font-size: var(--r-steps-description-font-size);
674
+ line-height: var(--text-caption--line-height);
675
675
  margin-top: 2px;
676
676
  }
677
677
 
@@ -1011,18 +1011,18 @@
1011
1011
 
1012
1012
  .r-combobox-select__control {
1013
1013
  box-sizing: border-box;
1014
- border: 1.5px solid var(--line-strong);
1015
- background: var(--layer-card);
1016
1014
  min-width: 0;
1017
- min-height: 44px;
1015
+ min-height: var(--control-height);
1016
+ border: 1px solid var(--line-field);
1017
+ background: var(--layer-card);
1018
1018
  color: var(--foreground);
1019
+ font-size: var(--field-font-size);
1020
+ line-height: var(--field-line-height);
1019
1021
  border-radius: 8px;
1020
1022
  outline: 0;
1021
1023
  align-items: center;
1022
1024
  gap: .5rem;
1023
1025
  padding: 0 6px 0 12px;
1024
- font-size: 16px;
1025
- line-height: 24px;
1026
1026
  transition: box-shadow 90ms ease-out;
1027
1027
  display: flex;
1028
1028
  }
@@ -1067,13 +1067,13 @@
1067
1067
  max-width: 100%;
1068
1068
  min-height: 28px;
1069
1069
  color: var(--foreground);
1070
+ font-size: var(--text-footnote);
1071
+ line-height: var(--text-footnote--line-height);
1070
1072
  border-radius: 6px;
1071
1073
  align-items: center;
1072
1074
  gap: 6px;
1073
1075
  padding: 0 2px 0 8px;
1074
- font-size: 13px;
1075
1076
  font-weight: 600;
1076
- line-height: 18px;
1077
1077
  display: inline-flex;
1078
1078
  overflow: hidden;
1079
1079
  }
@@ -1182,15 +1182,15 @@
1182
1182
  width: 100%;
1183
1183
  min-height: 36px;
1184
1184
  color: var(--foreground);
1185
+ font-size: var(--text-callout);
1186
+ line-height: var(--text-callout--line-height);
1185
1187
  border-radius: 8px;
1186
1188
  outline: 0;
1187
1189
  justify-content: space-between;
1188
1190
  align-items: center;
1189
1191
  gap: 10px;
1190
1192
  padding: 0 10px;
1191
- font-size: 14px;
1192
1193
  font-weight: 500;
1193
- line-height: 20px;
1194
1194
  transition: background-color .12s, color .12s;
1195
1195
  display: flex;
1196
1196
  overflow: hidden;
@@ -1235,25 +1235,25 @@
1235
1235
 
1236
1236
  .r-combobox-select__group-label {
1237
1237
  color: var(--foreground-hint);
1238
+ font-size: var(--text-caption);
1239
+ line-height: var(--text-caption--line-height);
1238
1240
  padding: 8px 10px 4px;
1239
- font-size: 12px;
1240
1241
  font-weight: 700;
1241
- line-height: 16px;
1242
1242
  }
1243
1243
 
1244
1244
  .r-combobox-select__message {
1245
1245
  color: var(--foreground-hint);
1246
+ font-size: var(--text-callout);
1247
+ line-height: var(--text-callout--line-height);
1246
1248
  text-align: start;
1247
1249
  padding: 14px 16px;
1248
- font-size: 14px;
1249
- line-height: 20px;
1250
1250
  }
1251
1251
 
1252
1252
  .r-combobox-select__overflow {
1253
1253
  color: var(--foreground-hint);
1254
1254
  font-family: var(--mono-font, ui-monospace, monospace);
1255
- font-size: 12px;
1256
- line-height: 16px;
1255
+ font-size: var(--text-caption);
1256
+ line-height: var(--text-caption--line-height);
1257
1257
  }
1258
1258
 
1259
1259
  .r-combobox-select__spinner {
@@ -1264,8 +1264,8 @@
1264
1264
 
1265
1265
  .r-combobox-select--sm.r-combobox-select__control {
1266
1266
  min-height: 1.75rem;
1267
- font-size: 13px;
1268
- line-height: 18px;
1267
+ font-size: var(--text-control);
1268
+ line-height: var(--text-control--line-height);
1269
1269
  }
1270
1270
 
1271
1271
  .r-combobox-select--sm .r-combobox-select__chips {
package/dist/styles.css CHANGED
@@ -75,7 +75,9 @@
75
75
 
76
76
  /* Raft Design System type styles (size / line-height / weight). Grotesk is
77
77
  what people say; pair the mono styles with `font-mono` for what machines
78
- write (ids, times, counts, events, code). */
78
+ write (ids, times, counts, events, code). These are the phone values; the
79
+ desktop block below the theme sets the working sizes again on a desktop
80
+ (from 768px with a mouse or trackpad). */
79
81
  --text-hero: 40px;
80
82
  --text-hero--line-height: 44px;
81
83
  --text-hero--font-weight: 700;
@@ -109,6 +111,11 @@
109
111
  --text-mono-meta--line-height: 16px;
110
112
  --text-mono-code: 13px;
111
113
  --text-mono-code--line-height: 20px;
114
+ /* The label of a 32px control: a Button at `md`, a panel action, a filter
115
+ trigger. Its own style, because on a phone it is footnote's 13px and on a
116
+ desktop it is 14px, where footnote drops to 12. */
117
+ --text-control: 13px;
118
+ --text-control--line-height: 18px;
112
119
 
113
120
  /* Deprecated utility names, kept until raft-ui 1.0 (see codemods/token-names.mjs). */
114
121
  --color-brutal-yellow: var(--palette-yellow-400);
@@ -123,6 +130,43 @@
123
130
  --color-brutal-cream: var(--palette-cream-200);
124
131
  }
125
132
 
133
+ /* Desktop type: from 768px (Tailwind's `md`, where the desktop layout begins)
134
+ with a mouse or trackpad, the same query as the desktop metrics in
135
+ foundation.css. Feishu's PC scale, 14/22 for reading, 12px for notes (on an
136
+ 18px line, see typography.md), 16/24 for titles. A phone, and a touch
137
+ screen at any width, keep the theme's values, Feishu's mobile scale. The
138
+ type utilities read these variables (`font-size: var(--text-body)`), so
139
+ every component follows without a breakpoint of its own. This block comes
140
+ after Tailwind's theme output, so it also wins over an app's own `@theme`
141
+ values for these names: an app that changes one sets it inside this query
142
+ as well. Hero, display, title and label stay as they are: setup pages and
143
+ the phone tab bar. */
144
+ @layer theme {
145
+ @media (width >= 48rem) and (pointer: fine) {
146
+ :root,
147
+ :host {
148
+ --text-headline: 16px;
149
+ --text-headline--line-height: 24px;
150
+ --text-body: 14px;
151
+ --text-body--line-height: 22px;
152
+ --text-subhead: 14px;
153
+ --text-subhead--line-height: 22px;
154
+ --text-callout: 14px;
155
+ --text-callout--line-height: 22px;
156
+ --text-footnote: 12px;
157
+ --text-footnote--line-height: 18px;
158
+ --text-caption: 12px;
159
+ --text-caption--line-height: 18px;
160
+ --text-mono-meta: 12px;
161
+ --text-mono-meta--line-height: 18px;
162
+ --text-mono-code: 13px;
163
+ --text-mono-code--line-height: 20px;
164
+ --text-control: 14px;
165
+ --text-control--line-height: 22px;
166
+ }
167
+ }
168
+ }
169
+
126
170
  @theme inline {
127
171
  --color-foreground: var(--foreground);
128
172
  --color-foreground-muted: var(--foreground-muted);
@@ -307,6 +351,20 @@
307
351
  --text-card-title: var(--card-title-font-size);
308
352
  --text-card-title--line-height: var(--card-title-line-height);
309
353
  --font-weight-card-title: var(--card-title-font-weight);
354
+ /* ─── Control metric axis ───────────────────────────────────────────────────
355
+ The height of a default control: 44px on a phone and on a touch screen,
356
+ 32px from 768px with a mouse or trackpad (Feishu's PC default). The
357
+ tokens switch in foundation.css, so a recipe writes `h-control` /
358
+ `size-control` and never a breakpoint. A button at this height keeps a 44px hit area with
359
+ `after:-inset-y-control-hit`, which is 0 on a phone.
360
+
361
+ Consumers: `Input`, `InputGroup`, `SelectTrigger`,
362
+ `ComboboxControl`, `ComboboxChips`, `ComboboxSelect`, and Button at `lg`,
363
+ `xl` (`h-control-cta`) and `icon-lg` (`size-control-icon` for its icon). */
364
+ --spacing-control: var(--control-height);
365
+ --spacing-control-cta: var(--control-height-cta);
366
+ --spacing-control-icon: var(--control-icon-size);
367
+ --spacing-control-hit: var(--control-hit-inset);
310
368
 
311
369
  /* Deprecated utility names, kept until raft-ui 1.0 (see codemods/token-names.mjs). */
312
370
  --color-foreground-strong: var(--foreground);
package/dist/tokens.json CHANGED
@@ -1588,6 +1588,7 @@
1588
1588
  },
1589
1589
  "field": {
1590
1590
  "$value": "{line.strong}",
1591
+ "$description": "The frame of a field and the composer: the ink of line-strong, drawn 1px.",
1591
1592
  "$extensions": {
1592
1593
  "build.botiverse.raft": {
1593
1594
  "scope": "every-mode"
@@ -2513,7 +2514,15 @@
2513
2514
  "value": 16,
2514
2515
  "unit": "px"
2515
2516
  },
2516
- "$type": "dimension"
2517
+ "$type": "dimension",
2518
+ "$extensions": {
2519
+ "build.botiverse.raft": {
2520
+ "desktop": {
2521
+ "value": 14,
2522
+ "unit": "px"
2523
+ }
2524
+ }
2525
+ }
2517
2526
  },
2518
2527
  "font-weight": {
2519
2528
  "$value": 400,
@@ -2524,7 +2533,15 @@
2524
2533
  "value": 24,
2525
2534
  "unit": "px"
2526
2535
  },
2527
- "$type": "dimension"
2536
+ "$type": "dimension",
2537
+ "$extensions": {
2538
+ "build.botiverse.raft": {
2539
+ "desktop": {
2540
+ "value": 22,
2541
+ "unit": "px"
2542
+ }
2543
+ }
2544
+ }
2528
2545
  }
2529
2546
  },
2530
2547
  "card-title": {
@@ -2533,7 +2550,15 @@
2533
2550
  "value": 16,
2534
2551
  "unit": "px"
2535
2552
  },
2536
- "$type": "dimension"
2553
+ "$type": "dimension",
2554
+ "$extensions": {
2555
+ "build.botiverse.raft": {
2556
+ "desktop": {
2557
+ "value": 14,
2558
+ "unit": "px"
2559
+ }
2560
+ }
2561
+ }
2537
2562
  },
2538
2563
  "font-weight": {
2539
2564
  "$value": 600,
@@ -2544,7 +2569,78 @@
2544
2569
  "value": 24,
2545
2570
  "unit": "px"
2546
2571
  },
2547
- "$type": "dimension"
2572
+ "$type": "dimension",
2573
+ "$extensions": {
2574
+ "build.botiverse.raft": {
2575
+ "desktop": {
2576
+ "value": 22,
2577
+ "unit": "px"
2578
+ }
2579
+ }
2580
+ }
2581
+ }
2582
+ },
2583
+ "control": {
2584
+ "$type": "dimension",
2585
+ "height": {
2586
+ "$value": {
2587
+ "value": 44,
2588
+ "unit": "px"
2589
+ },
2590
+ "$description": "A default control: a field, a Button at lg, an icon button at icon-lg. A finger's 44px on a phone, Feishu's 32px on a desktop.",
2591
+ "$extensions": {
2592
+ "build.botiverse.raft": {
2593
+ "desktop": {
2594
+ "value": 32,
2595
+ "unit": "px"
2596
+ }
2597
+ }
2598
+ }
2599
+ },
2600
+ "height-cta": {
2601
+ "$value": {
2602
+ "value": 52,
2603
+ "unit": "px"
2604
+ },
2605
+ "$description": "The full-width call to action across a sign-in, sign-up or setup form (Button xl).",
2606
+ "$extensions": {
2607
+ "build.botiverse.raft": {
2608
+ "desktop": {
2609
+ "value": 40,
2610
+ "unit": "px"
2611
+ }
2612
+ }
2613
+ }
2614
+ },
2615
+ "icon-size": {
2616
+ "$value": {
2617
+ "value": 22,
2618
+ "unit": "px"
2619
+ },
2620
+ "$description": "The icon in a control-height icon button.",
2621
+ "$extensions": {
2622
+ "build.botiverse.raft": {
2623
+ "desktop": {
2624
+ "value": 18,
2625
+ "unit": "px"
2626
+ }
2627
+ }
2628
+ }
2629
+ },
2630
+ "hit-inset": {
2631
+ "$value": {
2632
+ "value": 0,
2633
+ "unit": "px"
2634
+ },
2635
+ "$description": "How far a control-height button's hit area reaches past its top and bottom, to keep 44px.",
2636
+ "$extensions": {
2637
+ "build.botiverse.raft": {
2638
+ "desktop": {
2639
+ "value": 6,
2640
+ "unit": "px"
2641
+ }
2642
+ }
2643
+ }
2548
2644
  }
2549
2645
  },
2550
2646
  "sticker": {
package/dist/wip.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as ButtonProps, T as DialogTrigger, i as DialogClose, s as DialogContentProps, t as Dialog, w as DialogTitleProps } from "./index-DG62I-YM.mjs";
1
+ import { O as ButtonProps, T as DialogTrigger, i as DialogClose, s as DialogContentProps, t as Dialog, w as DialogTitleProps } from "./index-CDHLWJtB.mjs";
2
2
  import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
3
  import { Combobox } from "@base-ui/react/combobox";
4
4
  //#region src/components/combobox-select/combobox-select.types.d.ts
package/dist/wip.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as useTheme, f as DialogTitle, i as DialogContent, m as Button, p as DialogTrigger, r as DialogClose, t as Dialog, w as tv, x as useThemeScopeProps } from "./dialog-BYH5ojYr.mjs";
1
+ import { S as useTheme, f as DialogTitle, i as DialogContent, m as Button, p as DialogTrigger, r as DialogClose, t as Dialog, w as tv, x as useThemeScopeProps } from "./dialog-CDPAT3DH.mjs";
2
2
  import { Check, ChevronDown, LoaderCircle, Plus, X } from "lucide-react";
3
3
  import { useCallback, useId, useMemo, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.9.6",
3
+ "version": "0.10.1",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",