@viraui/react 2026.6.5 → 2026.7.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/catalog.json +12 -0
  2. package/dist/components/autocomplete/autocomplete.js +1 -1
  3. package/dist/components/basic-input/basic-input.d.ts +2 -1
  4. package/dist/components/basic-input/basic-input.js +7 -0
  5. package/dist/components/beam/beam-pulse-driver.d.ts +1 -0
  6. package/dist/components/beam/beam-pulse-driver.js +2 -2
  7. package/dist/components/beam/beam.css +1 -1
  8. package/dist/components/beam/beam.d.ts +5 -2
  9. package/dist/components/beam/beam.js +31 -26
  10. package/dist/components/dialog/dialog-sheet.js +1 -1
  11. package/dist/components/index.d.ts +2 -0
  12. package/dist/components/menu/menu-popup.js +1 -1
  13. package/dist/components/meter/meter.css +1 -1
  14. package/dist/components/meter/meter.d.ts +11 -1
  15. package/dist/components/meter/meter.js +2 -1
  16. package/dist/components/otp-field/index.d.ts +4 -0
  17. package/dist/components/otp-field/otp-field-input.d.ts +12 -0
  18. package/dist/components/otp-field/otp-field-input.js +29 -0
  19. package/dist/components/otp-field/otp-field-separator.d.ts +11 -0
  20. package/dist/components/otp-field/otp-field-separator.js +16 -0
  21. package/dist/components/otp-field/otp-field.css +1 -0
  22. package/dist/components/otp-field/otp-field.d.ts +49 -0
  23. package/dist/components/otp-field/otp-field.js +79 -0
  24. package/dist/components/otp-field/otp-field.module.js +23 -0
  25. package/dist/components/prose/index.d.ts +2 -0
  26. package/dist/components/prose/prose.css +1 -0
  27. package/dist/components/prose/prose.d.ts +40 -0
  28. package/dist/components/prose/prose.js +27 -0
  29. package/dist/components/prose/prose.module.js +5 -0
  30. package/dist/components/select/select.js +1 -1
  31. package/dist/components/separator/separator.d.ts +7 -0
  32. package/dist/components/separator/separator.js +2 -1
  33. package/dist/components/text/text.css +1 -1
  34. package/dist/components/text/text.d.ts +2 -1
  35. package/dist/components/text/text.js +4 -2
  36. package/dist/components/textfield/textfield.css +1 -1
  37. package/dist/components/textfield/textfield.d.ts +18 -10
  38. package/dist/components/textfield/textfield.js +43 -39
  39. package/dist/components/textfield/textfield.module.js +3 -1
  40. package/dist/components/title/title.css +1 -1
  41. package/dist/components/title/title.d.ts +2 -1
  42. package/dist/components/title/title.js +4 -2
  43. package/dist/components/toast/toast-banner.js +1 -1
  44. package/dist/index.js +5 -3
  45. package/dist/specs/components/beam/a11y.xml +3 -3
  46. package/dist/specs/components/beam/props.xml +22 -1
  47. package/dist/specs/components/beam/usage.xml +1 -0
  48. package/dist/specs/components/meter/patterns.xml +12 -0
  49. package/dist/specs/components/meter/props.xml +7 -0
  50. package/dist/specs/components/meter/usage.xml +2 -0
  51. package/dist/specs/components/otp-field/a11y.xml +10 -0
  52. package/dist/specs/components/otp-field/meta.xml +23 -0
  53. package/dist/specs/components/otp-field/patterns.xml +66 -0
  54. package/dist/specs/components/otp-field/props.xml +29 -0
  55. package/dist/specs/components/otp-field/usage.xml +13 -0
  56. package/dist/specs/components/prose/a11y.xml +7 -0
  57. package/dist/specs/components/prose/meta.xml +18 -0
  58. package/dist/specs/components/prose/patterns.xml +59 -0
  59. package/dist/specs/components/prose/props.xml +37 -0
  60. package/dist/specs/components/prose/usage.xml +12 -0
  61. package/dist/specs/components/separator/props.xml +3 -0
  62. package/dist/specs/components/separator/usage.xml +1 -0
  63. package/dist/specs/components/text/props.xml +2 -2
  64. package/dist/specs/components/text/usage.xml +1 -0
  65. package/dist/specs/components/textfield/meta.xml +1 -0
  66. package/dist/specs/components/textfield/props.xml +6 -1
  67. package/dist/specs/components/textfield/usage.xml +2 -1
  68. package/dist/specs/components/title/props.xml +2 -2
  69. package/dist/specs/components/title/usage.xml +1 -0
  70. package/dist/specs/index.xml +21 -1
  71. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{.viraui__otp-field-module__OTPField_8UOfz{--vui-textfield-input-height:var(--space-2xl);--vui-textfield-control-group-background:var(--base-1);--vui-textfield-control-group-color:var(--global-foreground);--vui-textfield-control-group-border-color:var(--global-contrast);--vui-textfield-control-group-border-radius:var(--radius-m);--vui-field-label-padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2);max-block-size:min-content;&[data-fullwidth=true]{inline-size:100%}&:has([required]) .viraui__otp-field-module__Label_7eZGm:after{content:" *";color:var(--highlight-red)}&[data-invalid][data-dirty] .viraui__otp-field-module__SlotCluster_nJxZ4:not(.viraui__otp-field-module__WiggleA_5-MVD,.viraui__otp-field-module__WiggleB_pfeA5){animation:viraui__otp-field-module__OTPFieldWiggleA_gLGT0 var(--duration-normal) var(--easing-wiggle)}}.viraui__otp-field-module__SlotCluster_nJxZ4{&.viraui__otp-field-module__WiggleA_5-MVD{animation:viraui__otp-field-module__OTPFieldWiggleA_gLGT0 var(--duration-normal) var(--easing-wiggle)}&.viraui__otp-field-module__WiggleB_pfeA5{animation:viraui__otp-field-module__OTPFieldWiggleB_0IC0k var(--duration-normal) var(--easing-wiggle)}@media (prefers-reduced-motion:reduce){animation:none}}@keyframes viraui__otp-field-module__OTPFieldWiggleA_gLGT0{0%{translate:var(--space-s)}to{translate:0}}@keyframes viraui__otp-field-module__OTPFieldWiggleB_0IC0k{0%{translate:var(--space-s)}to{translate:0}}.viraui__otp-field-module__Description_pYBbZ{padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2)}}@layer viraui.overrides{.viraui__otp-field-module__Slot_G3aie{text-align:center;padding-inline:0;aspect-ratio:1;&:focus::placeholder{color:#0000}}}
@@ -0,0 +1,49 @@
1
+ import { FieldRoot } from '@base-ui/react/field';
2
+ import { OTPFieldRoot as PrimitiveOTPFieldRoot } from '@base-ui/react/otp-field';
3
+ import { OTPFieldInputProps } from './otp-field-input';
4
+ import { OTPFieldSeparatorProps } from './otp-field-separator';
5
+ import * as React from 'react';
6
+ /**
7
+ * Public OTPField root props.
8
+ *
9
+ * Field chrome plus Base UI OTP Root. `length` is required and does not spawn Inputs —
10
+ * list exactly that many `OTPField.Input` children. `render` replaces the OTP primitive
11
+ * root (slot cluster), not the Field wrapper.
12
+ *
13
+ * @default Base UI pass-through props keep upstream defaults.
14
+ */
15
+ export type OTPFieldRootProps = PrimitiveOTPFieldRoot.Props & Omit<FieldRoot.Props, 'ref' | 'render'> & {
16
+ /**
17
+ * Visible label rendered with shared internal `FieldLabel`. Names the first slot.
18
+ *
19
+ * @defaultValue No label is rendered.
20
+ */
21
+ label?: React.ReactNode;
22
+ /**
23
+ * Helper copy rendered with `Field.Description` and `Text` while the field is valid.
24
+ *
25
+ * @defaultValue No description is rendered.
26
+ */
27
+ description?: React.ReactNode;
28
+ /**
29
+ * Error copy rendered with `Field.Error` and `Text` in the same slot as `description`.
30
+ * Replaces description while the field is invalid and dirty.
31
+ *
32
+ * @defaultValue No static error copy is rendered.
33
+ */
34
+ error?: React.ReactNode;
35
+ /**
36
+ * When `true`, the field is `inline-size: 100%` and the inner slot Stack uses
37
+ * `fullWidth` + `expandChildren` so slots grow equally.
38
+ *
39
+ * @defaultValue false
40
+ */
41
+ fullWidth?: boolean;
42
+ };
43
+ type OTPFieldComponent = React.FC<OTPFieldRootProps> & {
44
+ Root: React.FC<OTPFieldRootProps>;
45
+ Input: React.FC<OTPFieldInputProps>;
46
+ Separator: React.FC<OTPFieldSeparatorProps>;
47
+ };
48
+ export declare const OTPField: OTPFieldComponent;
49
+ export {};
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import './otp-field.css';
3
+ import { clsx } from "../../core/clsx.js";
4
+ import { FieldHelperCopy } from "../basic-input/field-helper-copy.js";
5
+ import { FieldLabel } from "../basic-input/field-label.js";
6
+ import { Stack } from "../stack/stack.js";
7
+ import otp_field_module_default from "./otp-field.module.js";
8
+ import { OTPFieldInput } from "./otp-field-input.js";
9
+ import { OTPFieldSeparator } from "./otp-field-separator.js";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { Field } from "@base-ui/react/field";
12
+ import * as React from "react";
13
+ import { OTPField } from "@base-ui/react/otp-field";
14
+ //#region src/components/otp-field/otp-field.tsx
15
+ var OTPFieldRoot = ({ children, className, description, dirty, disabled, error, fullWidth = false, invalid, label, name, onValueComplete, onValueInvalid, ref, render, touched, validate, validationDebounceTime, validationMode, ...otpRootProps }) => {
16
+ const [wigglePass, setWigglePass] = React.useState();
17
+ const bumpWiggle = () => {
18
+ setWigglePass((current) => current === "a" ? "b" : "a");
19
+ };
20
+ const handleValueInvalid = (value, eventDetails) => {
21
+ bumpWiggle();
22
+ onValueInvalid?.(value, eventDetails);
23
+ };
24
+ const handleValueComplete = (value, eventDetails) => {
25
+ if (invalid) bumpWiggle();
26
+ onValueComplete?.(value, eventDetails);
27
+ };
28
+ return /* @__PURE__ */ jsxs(Field.Root, {
29
+ className: clsx(otp_field_module_default.OTPField, className),
30
+ "data-fullwidth": fullWidth,
31
+ dirty,
32
+ disabled,
33
+ invalid,
34
+ name,
35
+ ref,
36
+ render: /* @__PURE__ */ jsx(Stack, {
37
+ expandChildren: true,
38
+ rowGap: "s"
39
+ }),
40
+ touched,
41
+ validate,
42
+ validationDebounceTime,
43
+ validationMode,
44
+ children: [
45
+ label && /* @__PURE__ */ jsx(Field.Label, {
46
+ className: otp_field_module_default.Label,
47
+ children: /* @__PURE__ */ jsx(FieldLabel, { children: label })
48
+ }),
49
+ /* @__PURE__ */ jsx(OTPField.Root, {
50
+ ...otpRootProps,
51
+ className: clsx(otp_field_module_default.SlotCluster, wigglePass === "a" && otp_field_module_default.WiggleA, wigglePass === "b" && otp_field_module_default.WiggleB),
52
+ disabled,
53
+ name,
54
+ onValueComplete: handleValueComplete,
55
+ onValueInvalid: handleValueInvalid,
56
+ render: render ?? /* @__PURE__ */ jsx(Stack, {
57
+ columnGap: "s",
58
+ direction: "row",
59
+ expandChildren: fullWidth,
60
+ fullWidth,
61
+ vAlign: "center"
62
+ }),
63
+ children
64
+ }),
65
+ /* @__PURE__ */ jsx(FieldHelperCopy, {
66
+ className: otp_field_module_default.Description,
67
+ description,
68
+ error
69
+ })
70
+ ]
71
+ });
72
+ };
73
+ var OTPField$1 = Object.assign(OTPFieldRoot, {
74
+ Root: OTPFieldRoot,
75
+ Input: OTPFieldInput,
76
+ Separator: OTPFieldSeparator
77
+ });
78
+ //#endregion
79
+ export { OTPField$1 as OTPField };
@@ -0,0 +1,23 @@
1
+ //#region src/components/otp-field/otp-field.module.css
2
+ var OTPField = "viraui__otp-field-module__OTPField_8UOfz";
3
+ var OTPFieldWiggleA = "viraui__otp-field-module__OTPFieldWiggleA_gLGT0";
4
+ var Label = "viraui__otp-field-module__Label_7eZGm";
5
+ var SlotCluster = "viraui__otp-field-module__SlotCluster_nJxZ4";
6
+ var WiggleA = "viraui__otp-field-module__WiggleA_5-MVD";
7
+ var WiggleB = "viraui__otp-field-module__WiggleB_pfeA5";
8
+ var OTPFieldWiggleB = "viraui__otp-field-module__OTPFieldWiggleB_0IC0k";
9
+ var Description = "viraui__otp-field-module__Description_pYBbZ";
10
+ var Slot = "viraui__otp-field-module__Slot_G3aie";
11
+ var otp_field_module_default = {
12
+ OTPField,
13
+ OTPFieldWiggleA,
14
+ Label,
15
+ SlotCluster,
16
+ WiggleA,
17
+ WiggleB,
18
+ OTPFieldWiggleB,
19
+ Description,
20
+ Slot
21
+ };
22
+ //#endregion
23
+ export { Description, Label, OTPField, OTPFieldWiggleA, OTPFieldWiggleB, Slot, SlotCluster, WiggleA, WiggleB, otp_field_module_default as default };
@@ -0,0 +1,2 @@
1
+ export { Prose } from './prose';
2
+ export type { ProseProps } from './prose';
@@ -0,0 +1 @@
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-prose-align{syntax:"*";inherits:false;initial-value:start}@property --vui-prose-gap-blocks{syntax:"<length>";inherits:true;initial-value:2em}@property --vui-prose-gap-heading-start{syntax:"<length>";inherits:true;initial-value:1.3em}@property --vui-prose-gap-heading-end{syntax:"<length>";inherits:true;initial-value:.8em}.viraui__prose-module__Prose_8yO9h{--vui-prose-gap-blocks:2em;--vui-prose-gap-heading-start:1.3em;--vui-prose-gap-heading-end:0.8em;&[data-prose-gap=s]{--vui-prose-gap-blocks:1em;--vui-prose-gap-heading-start:0.7em;--vui-prose-gap-heading-end:0.3em}&[data-prose-gap=m]{--vui-prose-gap-blocks:1.5em;--vui-prose-gap-heading-start:1em;--vui-prose-gap-heading-end:0.6em}&[data-prose-has-align=true]{text-align:var(--vui-prose-align)}}.viraui__prose-module__Prose_8yO9h>:is(h1,h2,h3,h4,h5,h6,hgroup,ul,ol,dd,p,figure,img,pre,table,fieldset,blockquote,picture,div):not(:first-child){margin-block:var(--vui-prose-gap-blocks)}.viraui__prose-module__Prose_8yO9h>:is(h1,h2,h3,h4,h5,h6):not(:last-child)+*{margin-block-start:var(--vui-prose-gap-heading-start)}.viraui__prose-module__Prose_8yO9h>:is(h1,h2,h3,h4,h5,h6){margin-block-end:var(--vui-prose-gap-heading-end);text-box:trim-both cap alphabetic}}
@@ -0,0 +1,40 @@
1
+ import { useRender } from '@base-ui/react/use-render';
2
+ import type * as React from 'react';
3
+ type ProseGap = 's' | 'm' | 'l';
4
+ /**
5
+ * Public Prose props.
6
+ *
7
+ * Grouping wrapper for document blocks. Native `div` attributes pass through.
8
+ * Replace the root with Base UI `render` (for example `article`). No `as` prop.
9
+ *
10
+ * @default Native div pass-through props keep React defaults.
11
+ */
12
+ export type ProseProps = React.ComponentPropsWithoutRef<'div'> & {
13
+ /**
14
+ * Em density for heading-aware vertical rhythm (`s` < `m` < `l`).
15
+ *
16
+ * @defaultValue 'l'
17
+ */
18
+ gap?: ProseGap;
19
+ /**
20
+ * Aligns the region using logical alignment values.
21
+ * Omit so `text-align` inherits from the parent. Title/Text children that omit `align` inherit this value.
22
+ *
23
+ * @default Inherits parent `text-align`.
24
+ */
25
+ align?: React.CSSProperties['textAlign'];
26
+ /**
27
+ * Replaces the default `div` root using Base UI render semantics.
28
+ *
29
+ * @default The prose region renders a `div` root.
30
+ */
31
+ render?: useRender.RenderProp;
32
+ /**
33
+ * Ref forwarded to the rendered prose root element.
34
+ *
35
+ * @default No ref is attached.
36
+ */
37
+ ref?: React.Ref<HTMLElement>;
38
+ };
39
+ export declare const Prose: React.FC<ProseProps>;
40
+ export {};
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import './prose.css';
3
+ import { clsx } from "../../core/clsx.js";
4
+ import prose_module_default from "./prose.module.js";
5
+ import { useRender } from "@base-ui/react/use-render";
6
+ //#region src/components/prose/prose.tsx
7
+ var Prose = ({ align, className, gap = "l", ref, render, style, ...otherProps }) => {
8
+ const hasAlign = align !== void 0;
9
+ const dynamicStyle = {
10
+ ...hasAlign && { "--vui-prose-align": align },
11
+ ...style
12
+ };
13
+ return useRender({
14
+ defaultTagName: "div",
15
+ render,
16
+ ref,
17
+ props: {
18
+ ...otherProps,
19
+ "data-prose-gap": gap,
20
+ "data-prose-has-align": hasAlign ? "true" : "false",
21
+ className: clsx(prose_module_default.Prose, className),
22
+ style: dynamicStyle
23
+ }
24
+ });
25
+ };
26
+ //#endregion
27
+ export { Prose };
@@ -0,0 +1,5 @@
1
+ //#region src/components/prose/prose.module.css
2
+ var Prose = "viraui__prose-module__Prose_8yO9h";
3
+ var prose_module_default = { Prose };
4
+ //#endregion
5
+ export { Prose, prose_module_default as default };
@@ -6,8 +6,8 @@ import { Text } from "../text/text.js";
6
6
  import { FieldHelperCopy } from "../basic-input/field-helper-copy.js";
7
7
  import { FieldLabel } from "../basic-input/field-label.js";
8
8
  import { Stack } from "../stack/stack.js";
9
- import { Elevator } from "../elevator/elevator.js";
10
9
  import { Surface } from "../surface/surface.js";
10
+ import { Elevator } from "../elevator/elevator.js";
11
11
  import { IconButton } from "../icon-button/icon-button.js";
12
12
  import select_module_default from "./select.module.js";
13
13
  import { SelectGroup } from "./select-group.js";
@@ -1,4 +1,5 @@
1
1
  import { useRender } from '@base-ui/react/use-render';
2
+ import { StackProps } from '../stack';
2
3
  import { ThemeTypes } from '@viraui/foundation/vira/types';
3
4
  import type * as React from 'react';
4
5
  type SeparatorAxisPadding = ThemeTypes['space'] | [ThemeTypes['space'] | 0, ThemeTypes['space'] | 0];
@@ -43,6 +44,12 @@ export type SeparatorProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'childr
43
44
  * @default No block padding.
44
45
  */
45
46
  vPadding?: SeparatorAxisPadding;
47
+ /**
48
+ * Caps the Stack wrapper inline size.
49
+ *
50
+ * @default No max width constraint.
51
+ */
52
+ maxWidth?: StackProps['maxWidth'];
46
53
  /**
47
54
  * Sets the line thickness preset.
48
55
  *
@@ -10,7 +10,7 @@ var separatorSizeValues = {
10
10
  m: 2,
11
11
  l: 3
12
12
  };
13
- var Separator = ({ className, hPadding, orientation = "horizontal", ref, render, size = "s", style, variant = "solid", vPadding, ...otherProps }) => {
13
+ var Separator = ({ className, hPadding, maxWidth, orientation = "horizontal", ref, render, size = "s", style, variant = "solid", vPadding, ...otherProps }) => {
14
14
  const dynamicStyle = {
15
15
  "--vui-separator-size": `${separatorSizeValues[size]}px`,
16
16
  "--vui-separator-border-style": variant,
@@ -30,6 +30,7 @@ var Separator = ({ className, hPadding, orientation = "horizontal", ref, render,
30
30
  ...otherProps,
31
31
  direction: orientation === "vertical" ? "row" : "column",
32
32
  hPadding,
33
+ maxWidth,
33
34
  vAlign: orientation === "vertical" ? "stretch" : "initial",
34
35
  vPadding,
35
36
  children: line
@@ -2,4 +2,4 @@
2
2
  var(--__text-size-min),
3
3
  calc(var(--__text-size-min) + (var(--__text-size-max) - var(--__text-size-min)) * var(--__text-fluid)),
4
4
  var(--__text-size-max)
5
- );--vui-text-font-family:var(--font-family-body);--vui-text-line-height:var(--font-leading-m);margin:0;max-inline-size:var(--vui-text-max-width);font-family:var(--vui-text-font-family);font-size:var(--vui-text-font-size);line-height:var(--vui-text-line-height);text-align:var(--vui-text-align);letter-spacing:var(--vui-text-letter-spacing);text-transform:var(--vui-text-text-transform);white-space:var(--vui-text-white-space);word-break:break-word;font-weight:var(--vui-text-font-weight);font-stretch:calc(var(--vui-text-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-text-font-weight),"wdth" var(--vui-text-font-width),"opsz" var(--vui-text-font-opsz);text-box:var(--vui-text-trim) var(--vui-text-trim-type);&[data-text-balanced=true]{text-wrap:balance}&[data-text-selection=false]{-webkit-user-select:none;user-select:none}&[data-text-has-padding=true]{padding:var(--vui-text-padding-block-start) var(--vui-text-padding-inline-end) var(--vui-text-padding-block-end) var(--vui-text-padding-inline-start)}&[data-text-family=mono]{--vui-text-font-family:var(--font-family-mono)}&[data-text-size="4xl"]{--__text-size-min:var(--font-scale-body-3xl);--__text-size-max:var(--font-scale-body-4xl)}&[data-text-size="3xl"]{--__text-size-min:var(--font-scale-body-2xl);--__text-size-max:var(--font-scale-body-3xl)}&[data-text-size="2xl"]{--__text-size-min:var(--font-scale-body-xl);--__text-size-max:var(--font-scale-body-2xl)}&[data-text-size=xl]{--__text-size-min:var(--font-scale-body-l);--__text-size-max:var(--font-scale-body-xl)}&[data-text-size=l]{--__text-size-min:var(--font-scale-body-m);--__text-size-max:var(--font-scale-body-l)}&[data-text-size=s]{--__text-size-max:var(--font-scale-body-s)}&[data-text-size=s],&[data-text-size=xs]{--__text-size-min:var(--font-scale-body-xs)}&[data-text-size=xs]{--__text-size-max:var(--font-scale-body-xs)}& .viraui__text-module__Text_bS8kv:not([data-text-size]){font-size:inherit}& .viraui__text-module__Text_bS8kv:not([data-text-line-height]){line-height:inherit}&[data-text-fluid=false]{--vui-text-font-size:var(--__text-size-max)}@container root style(--effect-fluid-typography: initial){--vui-text-font-size:var(--__text-size-max)}&[data-text-weight=light]{--vui-text-font-weight:200}&[data-text-weight=semibold]{--vui-text-font-weight:600}&[data-text-weight=bold]{--vui-text-font-weight:800}&[data-text-tone=primary]{color:var(--global-primary)}&[data-text-tone=muted]{color:var(--global-muted)}&[data-text-tone=interactive]{color:var(--global-interactive-text)}&[data-text-tone=positive]{color:var(--highlight-green)}&[data-text-tone=informative]{color:var(--highlight-blue)}&[data-text-tone=danger]{color:var(--highlight-red)}&[data-text-tone=warning]{color:var(--highlight-yellow)}&[data-text-has-literal-color=true]{color:var(--vui-text-literal-color)}&[data-text-line-height=none]{--vui-text-line-height:0}&[data-text-line-height=xs]{--vui-text-line-height:var(--font-leading-xs)}&[data-text-line-height=s]{--vui-text-line-height:var(--font-leading-s)}&[data-text-line-height=m]{--vui-text-line-height:var(--font-leading-m)}&[data-text-line-height=l]{--vui-text-line-height:var(--font-leading-l)}}}
5
+ );--vui-text-font-family:var(--font-family-body);--vui-text-line-height:var(--font-leading-m);margin:0;max-inline-size:var(--vui-text-max-width);font-family:var(--vui-text-font-family);font-size:var(--vui-text-font-size);line-height:var(--vui-text-line-height);letter-spacing:var(--vui-text-letter-spacing);text-transform:var(--vui-text-text-transform);white-space:var(--vui-text-white-space);word-break:break-word;font-weight:var(--vui-text-font-weight);font-stretch:calc(var(--vui-text-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-text-font-weight),"wdth" var(--vui-text-font-width),"opsz" var(--vui-text-font-opsz);text-box:var(--vui-text-trim) var(--vui-text-trim-type);&[data-text-balanced=true]{text-wrap:balance}&[data-text-selection=false]{-webkit-user-select:none;user-select:none}&[data-text-has-align=true]{text-align:var(--vui-text-align)}&[data-text-has-padding=true]{padding:var(--vui-text-padding-block-start) var(--vui-text-padding-inline-end) var(--vui-text-padding-block-end) var(--vui-text-padding-inline-start)}&[data-text-family=mono]{--vui-text-font-family:var(--font-family-mono)}&[data-text-size="4xl"]{--__text-size-min:var(--font-scale-body-3xl);--__text-size-max:var(--font-scale-body-4xl)}&[data-text-size="3xl"]{--__text-size-min:var(--font-scale-body-2xl);--__text-size-max:var(--font-scale-body-3xl)}&[data-text-size="2xl"]{--__text-size-min:var(--font-scale-body-xl);--__text-size-max:var(--font-scale-body-2xl)}&[data-text-size=xl]{--__text-size-min:var(--font-scale-body-l);--__text-size-max:var(--font-scale-body-xl)}&[data-text-size=l]{--__text-size-min:var(--font-scale-body-m);--__text-size-max:var(--font-scale-body-l)}&[data-text-size=s]{--__text-size-max:var(--font-scale-body-s)}&[data-text-size=s],&[data-text-size=xs]{--__text-size-min:var(--font-scale-body-xs)}&[data-text-size=xs]{--__text-size-max:var(--font-scale-body-xs)}& .viraui__text-module__Text_bS8kv:not([data-text-size]){font-size:inherit}& .viraui__text-module__Text_bS8kv:not([data-text-line-height]){line-height:inherit}&[data-text-fluid=false]{--vui-text-font-size:var(--__text-size-max)}@container root style(--effect-fluid-typography: initial){--vui-text-font-size:var(--__text-size-max)}&[data-text-weight=light]{--vui-text-font-weight:200}&[data-text-weight=semibold]{--vui-text-font-weight:600}&[data-text-weight=bold]{--vui-text-font-weight:800}&[data-text-tone=primary]{color:var(--global-primary)}&[data-text-tone=muted]{color:var(--global-muted)}&[data-text-tone=interactive]{color:var(--global-interactive-text)}&[data-text-tone=positive]{color:var(--highlight-green)}&[data-text-tone=informative]{color:var(--highlight-blue)}&[data-text-tone=danger]{color:var(--highlight-red)}&[data-text-tone=warning]{color:var(--highlight-yellow)}&[data-text-has-literal-color=true]{color:var(--vui-text-literal-color)}&[data-text-line-height=none]{--vui-text-line-height:0}&[data-text-line-height=xs]{--vui-text-line-height:var(--font-leading-xs)}&[data-text-line-height=s]{--vui-text-line-height:var(--font-leading-s)}&[data-text-line-height=m]{--vui-text-line-height:var(--font-leading-m)}&[data-text-line-height=l]{--vui-text-line-height:var(--font-leading-l)}}}
@@ -68,8 +68,9 @@ export type TextProps = React.ComponentPropsWithoutRef<'span'> & {
68
68
  lineHeight?: 'none' | 'xs' | 's' | 'm' | 'l';
69
69
  /**
70
70
  * Aligns text using logical alignment values.
71
+ * Omit so `text-align` inherits from the parent.
71
72
  *
72
- * @default 'start'
73
+ * @default Inherits parent `text-align`.
73
74
  */
74
75
  align?: React.CSSProperties['textAlign'];
75
76
  /**
@@ -4,10 +4,11 @@ import { resolveAxisPadding } from "../../core/styles/resolve-axis-padding.js";
4
4
  import text_module_default from "./text.module.js";
5
5
  import { useRender } from "@base-ui/react/use-render";
6
6
  //#region src/components/text/text.tsx
7
- var Text = ({ align = "start", balanced = false, selection = true, className, color, family = "body", fluid = false, fontWidth, hPadding, lineHeight, maxWidth, opticalSize, ref, render, size, style, tone, tracking, transform, trim = "both", trimType = "cap alphabetic", vPadding, weight = "regular", whiteSpace = "normal", ...otherProps }) => {
7
+ var Text = ({ align, balanced = false, selection = true, className, color, family = "body", fluid = false, fontWidth, hPadding, lineHeight, maxWidth, opticalSize, ref, render, size, style, tone, tracking, transform, trim = "both", trimType = "cap alphabetic", vPadding, weight = "regular", whiteSpace = "normal", ...otherProps }) => {
8
+ const hasAlign = align !== void 0;
8
9
  const hasPadding = Boolean(hPadding || vPadding);
9
10
  const dynamicStyle = {
10
- "--vui-text-align": align,
11
+ ...hasAlign && { "--vui-text-align": align },
11
12
  "--vui-text-max-width": maxWidth ?? "none",
12
13
  "--vui-text-literal-color": color,
13
14
  "--vui-text-white-space": whiteSpace,
@@ -36,6 +37,7 @@ var Text = ({ align = "start", balanced = false, selection = true, className, co
36
37
  "data-text-balanced": balanced ? "true" : "false",
37
38
  "data-text-selection": selection ? "true" : "false",
38
39
  "data-text-fluid": fluid ? "true" : "false",
40
+ "data-text-has-align": hasAlign ? "true" : "false",
39
41
  "data-text-has-padding": hasPadding ? "true" : "false",
40
42
  "data-text-has-literal-color": color ? "true" : "false",
41
43
  className: clsx(text_module_default.Text, className),
@@ -1 +1 @@
1
- @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-textfield-input-height{syntax:"<length>";inherits:true;initial-value:0}@property --vui-textfield-control-group-background{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-border-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-border-radius{syntax:"<length-percentage>";inherits:true;initial-value:0}.viraui__textfield-module__Textfield_o-70A{--vui-textfield-input-height:var(--space-2xl);--vui-textfield-control-group-background:var(--base-1);--vui-textfield-control-group-color:var(--global-foreground);--vui-textfield-control-group-border-color:var(--global-contrast);--vui-textfield-control-group-border-radius:var(--radius-m);--vui-field-label-padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2);max-block-size:min-content;&[data-fullwidth=true]{inline-size:100%}&:has([required]) .viraui__textfield-module__Label_5rjr0:after{content:" *";color:var(--highlight-red)}}.viraui__textfield-module__ControlGroup_2W-OC{box-sizing:border-box;inline-size:100%;block-size:round(to-zero,var(--vui-textfield-input-height),2px);background:var(--vui-textfield-control-group-background);color:var(--vui-textfield-control-group-color);border:1px solid var(--vui-textfield-control-group-border-color);border-radius:var(--vui-textfield-control-group-border-radius);&:has(.viraui__textfield-module__CustomBasicInput_oHgtd:focus-visible),&:has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-focused]){outline:var(--outline);outline-offset:var(--outline-offset)}&:has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-invalid][data-dirty]){--vui-textfield-control-group-border-color:var(--highlight-red);box-shadow:none;outline-color:oklab(from var(--highlight-red) l a b/.5)}&:has(.viraui__textfield-module__CustomBasicInput_oHgtd:disabled),&:has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-disabled]){--vui-textfield-control-group-background:var(--global-disabled-background);--vui-textfield-control-group-border-color:#0000;--vui-textfield-control-group-color:var(--global-disabled-foreground);box-shadow:none;cursor:not-allowed}&:has(.viraui__textfield-module__Addon_kK1ef[data-position=start])>.viraui__textfield-module__CustomBasicInput_oHgtd{padding-inline-start:var(--space-xs)}&:has(.viraui__textfield-module__Addon_kK1ef[data-position=end])>.viraui__textfield-module__CustomBasicInput_oHgtd{padding-inline-end:var(--space-xs)}}.viraui__textfield-module__Addon_kK1ef{min-inline-size:var(--space-xl)}.viraui__textfield-module__Description_8Qxot{padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2)}}@layer viraui.overrides{.viraui__textfield-module__CustomBasicInput_oHgtd{flex:1 1 auto;block-size:100%;line-height:1.2;background:#0000;border-radius:inherit;&:not(:-webkit-autofill){box-shadow:none}&:focus-visible{outline:none}}}
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-textfield-input-height{syntax:"<length>";inherits:true;initial-value:0}@property --vui-textfield-control-group-background{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-border-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-textfield-control-group-border-radius{syntax:"<length-percentage>";inherits:true;initial-value:0}.viraui__textfield-module__ControlOnly_22bXm,.viraui__textfield-module__Textfield_o-70A{--vui-textfield-input-height:var(--space-2xl);--vui-textfield-control-group-background:var(--base-1);--vui-textfield-control-group-color:var(--global-foreground);--vui-textfield-control-group-border-color:var(--global-contrast);--vui-textfield-control-group-border-radius:var(--radius-m)}.viraui__textfield-module__Textfield_o-70A{--vui-field-label-padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2);max-block-size:min-content;&[data-fullwidth=true]{inline-size:100%}&:has([required]) .viraui__textfield-module__Label_5rjr0:after{content:" *";color:var(--highlight-red)}}.viraui__textfield-module__ControlGroup_2W-OC{box-sizing:border-box;inline-size:100%;block-size:round(to-zero,var(--vui-textfield-input-height),2px);background:var(--vui-textfield-control-group-background);color:var(--vui-textfield-control-group-color);border:1px solid var(--vui-textfield-control-group-border-color);border-radius:var(--vui-textfield-control-group-border-radius);&:has(.viraui__textfield-module__CustomBasicInput_oHgtd:focus),&:has(.viraui__textfield-module__CustomBasicInput_oHgtd:focus-visible),&:not(.viraui__textfield-module__ControlOnly_22bXm):has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-focused]){outline:var(--outline);outline-offset:var(--outline-offset)}&:has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-invalid][data-dirty]){--vui-textfield-control-group-border-color:var(--highlight-red);box-shadow:none;outline-color:oklab(from var(--highlight-red) l a b/.5)}&:has(.viraui__textfield-module__CustomBasicInput_oHgtd:disabled),&:has(.viraui__textfield-module__CustomBasicInput_oHgtd[data-disabled]){--vui-textfield-control-group-background:var(--global-disabled-background);--vui-textfield-control-group-border-color:#0000;--vui-textfield-control-group-color:var(--global-disabled-foreground);box-shadow:none;cursor:not-allowed}&:has(.viraui__textfield-module__Addon_kK1ef[data-position=start])>.viraui__textfield-module__CustomBasicInput_oHgtd{padding-inline-start:var(--space-xs)}&:has(.viraui__textfield-module__Addon_kK1ef[data-position=end])>.viraui__textfield-module__CustomBasicInput_oHgtd{padding-inline-end:var(--space-xs)}}.viraui__textfield-module__ControlOnly_22bXm{inline-size:max-content;min-inline-size:0}.viraui__textfield-module__Addon_kK1ef{min-inline-size:var(--space-xl)}.viraui__textfield-module__Description_8Qxot{padding-inline:calc(var(--vui-textfield-control-group-border-radius) / 2)}}@layer viraui.overrides{.viraui__textfield-module__CustomBasicInput_oHgtd{flex:1 1 auto;block-size:100%;line-height:1.2;background:#0000;border-radius:inherit;&:not(:-webkit-autofill){box-shadow:none}&:focus,&:focus-visible{outline:none}}}
@@ -6,22 +6,22 @@ import type * as React from 'react';
6
6
  * Public Textfield props.
7
7
  *
8
8
  * Includes Base UI input props such as `value`, `defaultValue`, `placeholder`, `disabled`,
9
- * and event handlers, plus optional `label`, `description`, and `error` copy rendered with
10
- * `Text` inside a `Stack` field layout.
9
+ * `size`, and event handlers, plus optional `label`, `description`, and `error` copy rendered with
10
+ * `Text` inside a `Stack` field layout. `controlOnly` skips Field chrome for compose inside another Field.
11
11
  *
12
12
  * @default Base UI pass-through props keep upstream defaults.
13
13
  */
14
- export type TextfieldProps = PrimitiveInput.Props & FieldRoot.Props & {
14
+ export type TextfieldProps = PrimitiveInput.Props & Omit<FieldRoot.Props, 'render' | keyof PrimitiveInput.Props> & {
15
15
  /**
16
16
  * Visible label rendered with shared internal `FieldLabel`.
17
17
  *
18
- * @default No label is rendered.
18
+ * @defaultValue No label is rendered.
19
19
  */
20
20
  label?: React.ReactNode;
21
21
  /**
22
22
  * Helper copy rendered with `Field.Description` and `Text` while the field is valid.
23
23
  *
24
- * @default No description is rendered.
24
+ * @defaultValue No description is rendered.
25
25
  */
26
26
  description?: React.ReactNode;
27
27
  /**
@@ -29,34 +29,42 @@ export type TextfieldProps = PrimitiveInput.Props & FieldRoot.Props & {
29
29
  * Replaces description while the field is invalid and dirty. Validation messages from `validate`
30
30
  * are shown by `Field.Error` even when this prop is omitted.
31
31
  *
32
- * @default No static error copy is rendered.
32
+ * @defaultValue No static error copy is rendered.
33
33
  */
34
34
  error?: React.ReactNode;
35
35
  /**
36
36
  * Leading decoration (prefix text, icon). Not a second field control.
37
37
  *
38
- * @default No start addon is rendered.
38
+ * @defaultValue No start addon is rendered.
39
39
  */
40
40
  startAddon?: React.ReactNode;
41
41
  /**
42
42
  * Trailing decoration (icon, button). Buttons must use `type="button"`.
43
43
  *
44
- * @default No end addon is rendered.
44
+ * @defaultValue No end addon is rendered.
45
45
  */
46
46
  endAddon?: React.ReactNode;
47
47
  /**
48
48
  * When `true`, sets `field-sizing: content` so the control grows inline with its value.
49
49
  * Block chrome stays `var(--vui-textfield-input-height)`. Omitted / `false` keeps the browser fixed default.
50
50
  *
51
- * @default undefined (browser fixed)
51
+ * @defaultValue undefined (browser fixed)
52
52
  */
53
53
  fitContent?: BasicInputProps['fitContent'];
54
54
  /**
55
55
  * When `true`, root and control group take `inline-size: 100%`.
56
56
  * Prefer this prop over CSS `width: 100%` / `w-full` on the Textfield root.
57
57
  *
58
- * @default false
58
+ * @defaultValue false
59
59
  */
60
60
  fullWidth?: boolean;
61
+ /**
62
+ * When `true`, skip `Field.Root` / label / helper. Control group + input only.
63
+ * Use inside another Field (OTP slots). Native `size` and `fitContent` still apply.
64
+ * `label`, `description`, and `error` are ignored.
65
+ *
66
+ * @defaultValue false
67
+ */
68
+ controlOnly?: boolean;
61
69
  };
62
70
  export declare const Textfield: React.FC<TextfieldProps>;
@@ -10,46 +10,50 @@ import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Field } from "@base-ui/react/field";
11
11
  import { Input } from "@base-ui/react/input";
12
12
  //#region src/components/textfield/textfield.tsx
13
- var Textfield = ({ className, description, dirty, disabled, endAddon, error, fitContent, fullWidth = false, invalid, label, name, ref, startAddon, touched, validate, validationDebounceTime, validationMode, ...inputProps }) => /* @__PURE__ */ jsxs(Field.Root, {
14
- className: clsx(textfield_module_default.Textfield, className),
15
- dirty,
16
- disabled,
17
- invalid,
18
- name,
19
- touched,
20
- validate,
21
- validationDebounceTime,
22
- validationMode,
23
- "data-fullwidth": fullWidth,
24
- render: /* @__PURE__ */ jsx(Stack, {
25
- expandChildren: true,
26
- rowGap: "s"
27
- }),
28
- children: [
29
- label && /* @__PURE__ */ jsx(Field.Label, {
30
- className: textfield_module_default.Label,
31
- children: /* @__PURE__ */ jsx(FieldLabel, { children: label })
13
+ var Textfield = ({ className, controlOnly = false, description, dirty, disabled, endAddon, error, fitContent, fullWidth = false, invalid, label, name, ref, render, startAddon, touched, validate, validationDebounceTime, validationMode, ...inputProps }) => {
14
+ const control = /* @__PURE__ */ jsx(InputControlGroup, {
15
+ addonClassName: textfield_module_default.Addon,
16
+ controlGroupClassName: clsx(textfield_module_default.ControlGroup, controlOnly && textfield_module_default.ControlOnly),
17
+ endAddon,
18
+ startAddon,
19
+ children: /* @__PURE__ */ jsx(BasicInput, {
20
+ ...inputProps,
21
+ ref,
22
+ className: clsx(textfield_module_default.CustomBasicInput, controlOnly && className),
23
+ disabled,
24
+ fitContent,
25
+ render: render ?? /* @__PURE__ */ jsx(Input, {})
26
+ })
27
+ });
28
+ if (controlOnly) return control;
29
+ return /* @__PURE__ */ jsxs(Field.Root, {
30
+ className: clsx(textfield_module_default.Textfield, className),
31
+ "data-fullwidth": fullWidth,
32
+ dirty,
33
+ disabled,
34
+ invalid,
35
+ name,
36
+ render: /* @__PURE__ */ jsx(Stack, {
37
+ expandChildren: true,
38
+ rowGap: "s"
32
39
  }),
33
- /* @__PURE__ */ jsx(InputControlGroup, {
34
- addonClassName: textfield_module_default.Addon,
35
- controlGroupClassName: textfield_module_default.ControlGroup,
36
- endAddon,
37
- startAddon,
38
- children: /* @__PURE__ */ jsx(BasicInput, {
39
- ...inputProps,
40
- ref,
41
- className: textfield_module_default.CustomBasicInput,
42
- disabled,
43
- fitContent,
44
- render: /* @__PURE__ */ jsx(Input, {})
40
+ touched,
41
+ validate,
42
+ validationDebounceTime,
43
+ validationMode,
44
+ children: [
45
+ label && /* @__PURE__ */ jsx(Field.Label, {
46
+ className: textfield_module_default.Label,
47
+ children: /* @__PURE__ */ jsx(FieldLabel, { children: label })
48
+ }),
49
+ control,
50
+ /* @__PURE__ */ jsx(FieldHelperCopy, {
51
+ className: textfield_module_default.Description,
52
+ description,
53
+ error
45
54
  })
46
- }),
47
- /* @__PURE__ */ jsx(FieldHelperCopy, {
48
- className: textfield_module_default.Description,
49
- description,
50
- error
51
- })
52
- ]
53
- });
55
+ ]
56
+ });
57
+ };
54
58
  //#endregion
55
59
  export { Textfield };
@@ -1,5 +1,6 @@
1
1
  //#region src/components/textfield/textfield.module.css
2
2
  var Textfield = "viraui__textfield-module__Textfield_o-70A";
3
+ var ControlOnly = "viraui__textfield-module__ControlOnly_22bXm";
3
4
  var Label = "viraui__textfield-module__Label_5rjr0";
4
5
  var ControlGroup = "viraui__textfield-module__ControlGroup_2W-OC";
5
6
  var CustomBasicInput = "viraui__textfield-module__CustomBasicInput_oHgtd";
@@ -7,6 +8,7 @@ var Addon = "viraui__textfield-module__Addon_kK1ef";
7
8
  var Description = "viraui__textfield-module__Description_8Qxot";
8
9
  var textfield_module_default = {
9
10
  Textfield,
11
+ ControlOnly,
10
12
  Label,
11
13
  ControlGroup,
12
14
  CustomBasicInput,
@@ -14,4 +16,4 @@ var textfield_module_default = {
14
16
  Description
15
17
  };
16
18
  //#endregion
17
- export { Addon, ControlGroup, CustomBasicInput, Description, Label, Textfield, textfield_module_default as default };
19
+ export { Addon, ControlGroup, ControlOnly, CustomBasicInput, Description, Label, Textfield, textfield_module_default as default };
@@ -2,4 +2,4 @@
2
2
  var(--__title-size-min),
3
3
  calc(var(--__title-size-min) + (var(--__title-size-max) - var(--__title-size-min)) * var(--__title-fluid)),
4
4
  var(--__title-size-max)
5
- );--vui-title-font-family:var(--font-family-heading);--vui-title-line-height:var(--font-leading-m);--__title-letter-spacing:-0.03em;margin:0;max-inline-size:var(--vui-title-max-width);font-family:var(--vui-title-font-family);font-size:var(--vui-title-font-size);line-height:var(--vui-title-line-height);letter-spacing:var(--__title-letter-spacing);text-transform:var(--vui-title-text-transform);text-align:var(--vui-title-align);white-space:var(--vui-title-white-space);word-break:break-word;font-weight:var(--vui-title-font-weight);font-stretch:calc(var(--vui-title-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-title-font-weight),"wdth" var(--vui-title-font-width),"opsz" var(--vui-title-font-opsz);text-box:var(--vui-title-trim) var(--vui-title-trim-type);display:inline-flex;&[data-title-has-tracking=true]{letter-spacing:var(--vui-title-letter-spacing)}&[data-title-balanced=true]{text-wrap:balance}&[data-title-selection=false]{-webkit-user-select:none;user-select:none}&[data-title-has-padding=true]{padding:var(--vui-title-padding-block-start) var(--vui-title-padding-inline-end) var(--vui-title-padding-block-end) var(--vui-title-padding-inline-start)}&[data-title-size=display]{--__title-size-min:var(--font-scale-h1);--__title-size-max:var(--font-scale-display)}&[data-title-size="2"]{--__title-size-min:var(--font-scale-h3);--__title-size-max:var(--font-scale-h2)}&[data-title-size="3"]{--__title-size-min:var(--font-scale-h4);--__title-size-max:var(--font-scale-h3)}&[data-title-size="4"]{--__title-size-min:var(--font-scale-h5);--__title-size-max:var(--font-scale-h4)}&[data-title-size="5"]{--__title-size-min:var(--font-scale-h6);--__title-size-max:var(--font-scale-h5);--__title-letter-spacing:-0.02em}&[data-title-size="6"]{--__title-size-min:var(--font-scale-body-m);--__title-size-max:var(--font-scale-h6);--__title-letter-spacing:-0.02em}&[data-title-fluid=false]{--vui-title-font-size:var(--__title-size-max)}@container root style(--effect-fluid-typography: initial){--vui-title-font-size:var(--__title-size-max)}&[data-title-has-literal-color=true]{color:var(--vui-title-literal-color)}&[data-title-line-height=none]{--vui-title-line-height:0}&[data-title-line-height=xs]{--vui-title-line-height:var(--font-leading-xs)}&[data-title-line-height=s]{--vui-title-line-height:var(--font-leading-s)}&[data-title-line-height=m]{--vui-title-line-height:var(--font-leading-m)}&[data-title-line-height=l]{--vui-title-line-height:var(--font-leading-l)}}}
5
+ );--vui-title-font-family:var(--font-family-heading);--vui-title-line-height:var(--font-leading-m);--__title-letter-spacing:-0.03em;margin:0;max-inline-size:var(--vui-title-max-width);font-family:var(--vui-title-font-family);font-size:var(--vui-title-font-size);line-height:var(--vui-title-line-height);letter-spacing:var(--__title-letter-spacing);text-transform:var(--vui-title-text-transform);white-space:var(--vui-title-white-space);word-break:break-word;font-weight:var(--vui-title-font-weight);font-stretch:calc(var(--vui-title-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-title-font-weight),"wdth" var(--vui-title-font-width),"opsz" var(--vui-title-font-opsz);text-box:var(--vui-title-trim) var(--vui-title-trim-type);display:inline-flex;&[data-title-has-align=true]{text-align:var(--vui-title-align)}&[data-title-has-tracking=true]{letter-spacing:var(--vui-title-letter-spacing)}&[data-title-balanced=true]{text-wrap:balance}&[data-title-selection=false]{-webkit-user-select:none;user-select:none}&[data-title-has-padding=true]{padding:var(--vui-title-padding-block-start) var(--vui-title-padding-inline-end) var(--vui-title-padding-block-end) var(--vui-title-padding-inline-start)}&[data-title-size=display]{--__title-size-min:var(--font-scale-h1);--__title-size-max:var(--font-scale-display)}&[data-title-size="2"]{--__title-size-min:var(--font-scale-h3);--__title-size-max:var(--font-scale-h2)}&[data-title-size="3"]{--__title-size-min:var(--font-scale-h4);--__title-size-max:var(--font-scale-h3)}&[data-title-size="4"]{--__title-size-min:var(--font-scale-h5);--__title-size-max:var(--font-scale-h4)}&[data-title-size="5"]{--__title-size-min:var(--font-scale-h6);--__title-size-max:var(--font-scale-h5);--__title-letter-spacing:-0.02em}&[data-title-size="6"]{--__title-size-min:var(--font-scale-body-m);--__title-size-max:var(--font-scale-h6);--__title-letter-spacing:-0.02em}&[data-title-fluid=false]{--vui-title-font-size:var(--__title-size-max)}@container root style(--effect-fluid-typography: initial){--vui-title-font-size:var(--__title-size-max)}&[data-title-has-literal-color=true]{color:var(--vui-title-literal-color)}&[data-title-line-height=none]{--vui-title-line-height:0}&[data-title-line-height=xs]{--vui-title-line-height:var(--font-leading-xs)}&[data-title-line-height=s]{--vui-title-line-height:var(--font-leading-s)}&[data-title-line-height=m]{--vui-title-line-height:var(--font-leading-m)}&[data-title-line-height=l]{--vui-title-line-height:var(--font-leading-l)}}}
@@ -55,8 +55,9 @@ export type TitleProps = React.ComponentPropsWithoutRef<'span'> & {
55
55
  lineHeight?: 'none' | 'xs' | 's' | 'm' | 'l';
56
56
  /**
57
57
  * Aligns text using logical alignment values.
58
+ * Omit so `text-align` inherits from the parent.
58
59
  *
59
- * @default 'start'
60
+ * @default Inherits parent `text-align`.
60
61
  */
61
62
  align?: React.CSSProperties['textAlign'];
62
63
  /**
@@ -4,11 +4,12 @@ import { resolveAxisPadding } from "../../core/styles/resolve-axis-padding.js";
4
4
  import title_module_default from "./title.module.js";
5
5
  import { useRender } from "@base-ui/react/use-render";
6
6
  //#region src/components/title/title.tsx
7
- var Title = ({ align = "start", balanced = false, selection = true, className, color, fluid = true, fontWidth, hPadding, size = "1", lineHeight = "m", maxWidth, opticalSize, ref, render, style, tracking, transform, trim = "both", trimType = "cap alphabetic", vPadding, weight, whiteSpace = "normal", ...otherProps }) => {
7
+ var Title = ({ align, balanced = false, selection = true, className, color, fluid = true, fontWidth, hPadding, size = "1", lineHeight = "m", maxWidth, opticalSize, ref, render, style, tracking, transform, trim = "both", trimType = "cap alphabetic", vPadding, weight, whiteSpace = "normal", ...otherProps }) => {
8
+ const hasAlign = align !== void 0;
8
9
  const hasPadding = Boolean(hPadding || vPadding);
9
10
  const hasTracking = tracking !== void 0;
10
11
  const dynamicStyle = {
11
- "--vui-title-align": align,
12
+ ...hasAlign && { "--vui-title-align": align },
12
13
  "--vui-title-max-width": maxWidth ?? "none",
13
14
  "--vui-title-literal-color": color,
14
15
  "--vui-title-white-space": whiteSpace,
@@ -34,6 +35,7 @@ var Title = ({ align = "start", balanced = false, selection = true, className, c
34
35
  "data-title-balanced": balanced ? "true" : "false",
35
36
  "data-title-selection": selection ? "true" : "false",
36
37
  "data-title-fluid": fluid ? "true" : "false",
38
+ "data-title-has-align": hasAlign ? "true" : "false",
37
39
  "data-title-has-padding": hasPadding ? "true" : "false",
38
40
  "data-title-has-literal-color": color ? "true" : "false",
39
41
  "data-title-has-tracking": hasTracking ? "true" : "false",
@@ -1,8 +1,8 @@
1
1
  import { clsx } from "../../core/clsx.js";
2
2
  import { Text } from "../text/text.js";
3
3
  import { Stack } from "../stack/stack.js";
4
- import { Button } from "../button/button.js";
5
4
  import { Surface } from "../surface/surface.js";
5
+ import { Button } from "../button/button.js";
6
6
  import toast_module_default from "./toast.module.js";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Toast } from "@base-ui/react/toast";