@viraui/react 2026.9.0 → 2026.9.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/dist/catalog.json +3 -3
- package/dist/components/prose/prose.css +1 -1
- package/dist/components/prose/prose.d.ts +8 -0
- package/dist/components/prose/prose.js +2 -1
- package/dist/components/text/text.css +2 -2
- package/dist/components/text/text.d.ts +9 -6
- package/dist/components/text/text.js +2 -2
- package/dist/components/title/title.css +2 -2
- package/dist/components/title/title.d.ts +9 -6
- package/dist/components/title/title.js +2 -2
- package/dist/specs/components/prose/meta.xml +1 -1
- package/dist/specs/components/prose/patterns.xml +1 -1
- package/dist/specs/components/prose/props.xml +8 -2
- package/dist/specs/components/prose/usage.xml +2 -1
- package/dist/specs/components/text/meta.xml +1 -1
- package/dist/specs/components/text/patterns.xml +11 -11
- package/dist/specs/components/text/props.xml +10 -7
- package/dist/specs/components/text/usage.xml +2 -2
- package/dist/specs/components/title/meta.xml +1 -1
- package/dist/specs/components/title/patterns.xml +8 -8
- package/dist/specs/components/title/props.xml +10 -7
- package/dist/specs/components/title/usage.xml +3 -3
- package/dist/specs/index.xml +4 -4
- package/package.json +2 -2
package/dist/catalog.json
CHANGED
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
{
|
|
186
186
|
"id": "prose",
|
|
187
187
|
"name": "Prose",
|
|
188
|
-
"summary": "Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm
|
|
188
|
+
"summary": "Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm, region `wrap` (default `pretty`), and reading column via parent Stack `maxWidth` sized for body Text `size`.",
|
|
189
189
|
"agentDocs": "./specs/components/prose/meta.xml"
|
|
190
190
|
},
|
|
191
191
|
{
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
{
|
|
264
264
|
"id": "text",
|
|
265
265
|
"name": "Text",
|
|
266
|
-
"summary": "Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `
|
|
266
|
+
"summary": "Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `wrap`).",
|
|
267
267
|
"agentDocs": "./specs/components/text/meta.xml"
|
|
268
268
|
},
|
|
269
269
|
{
|
|
@@ -281,7 +281,7 @@
|
|
|
281
281
|
{
|
|
282
282
|
"id": "title",
|
|
283
283
|
"name": "Title",
|
|
284
|
-
"summary": "Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `
|
|
284
|
+
"summary": "Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `wrap`).",
|
|
285
285
|
"agentDocs": "./specs/components/title/meta.xml"
|
|
286
286
|
},
|
|
287
287
|
{
|
|
@@ -1 +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}
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-prose-align{syntax:"*";inherits:false;initial-value:start}@property --vui-prose-wrap{syntax:"*";inherits:true;initial-value:pretty}@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}text-wrap:var(--vui-prose-wrap);&[data-prose-has-align=true]{text-align:var(--vui-prose-align)}&>:is(h1,h2,h3,h4,h5,h6,hgroup,ul,ol,dd,p,figure,img,pre,table,fieldset,blockquote,picture,div):where(:not(:first-child)){margin-block:var(--vui-prose-gap-blocks)}&>:is(h1,h2,h3,h4,h5,h6)+*{margin-block-start:var(--vui-prose-gap-heading-start)}&>:is(h1,h2,h3,h4,h5,h6){margin-block-end:var(--vui-prose-gap-heading-end);text-box:trim-both cap alphabetic}}}
|
|
@@ -25,6 +25,14 @@ export type ProseProps = React.ComponentPropsWithoutRef<'div'> & {
|
|
|
25
25
|
* @defaultValue Inherits parent `text-align`.
|
|
26
26
|
*/
|
|
27
27
|
align?: React.CSSProperties['textAlign'];
|
|
28
|
+
/**
|
|
29
|
+
* Controls CSS `text-wrap` on the Prose root (inherits to descendants unless a child overrides).
|
|
30
|
+
* Default `pretty` for orphan-aware article walls. Prefer `balance` only on short multi-line
|
|
31
|
+
* blocks that need even line lengths. Single-line metrics stay on child Text/Title `whiteSpace`.
|
|
32
|
+
*
|
|
33
|
+
* @defaultValue 'pretty'
|
|
34
|
+
*/
|
|
35
|
+
wrap?: React.CSSProperties['textWrap'];
|
|
28
36
|
/**
|
|
29
37
|
* Replaces the default `div` root using Base UI render semantics.
|
|
30
38
|
*
|
|
@@ -4,10 +4,11 @@ import { clsx } from "../../core/clsx.js";
|
|
|
4
4
|
import prose_module_default from "./prose.module.js";
|
|
5
5
|
import { useRender } from "@base-ui/react/use-render";
|
|
6
6
|
//#region src/components/prose/prose.tsx
|
|
7
|
-
var Prose = ({ align, className, gap = "l", ref, render, style, ...otherProps }) => {
|
|
7
|
+
var Prose = ({ align, className, gap = "l", ref, render, style, wrap = "pretty", ...otherProps }) => {
|
|
8
8
|
const hasAlign = align !== void 0;
|
|
9
9
|
const dynamicStyle = {
|
|
10
10
|
...hasAlign && { "--vui-prose-align": align },
|
|
11
|
+
"--vui-prose-wrap": wrap,
|
|
11
12
|
...style
|
|
12
13
|
};
|
|
13
14
|
return useRender({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-text-align{syntax:"*";inherits:false;initial-value:start}@property --vui-text-max-width{syntax:"*";inherits:false;initial-value:none}@property --vui-text-literal-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-text-white-space{syntax:"*";inherits:false;initial-value:normal}@property --vui-text-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --vui-text-text-transform{syntax:"none | capitalize | uppercase | lowercase";inherits:false;initial-value:none}@property --vui-text-font-weight{syntax:"<number>";inherits:false;initial-value:400}@property --vui-text-font-width{syntax:"<number>";inherits:false;initial-value:100}@property --vui-text-font-opsz{syntax:"<number>";inherits:false;initial-value:16}@property --vui-text-trim{syntax:"*";inherits:false;initial-value:trim-both}@property --vui-text-trim-type{syntax:"*";inherits:false;initial-value:cap alphabetic}@property --vui-text-padding-inline-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-inline-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-block-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-block-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-font-size{syntax:"<length>";inherits:false;initial-value:1rem}@property --vui-text-font-family{syntax:"*";inherits:false;initial-value:sans-serif}@property --vui-text-line-height{syntax:"normal | <number> | <length-percentage>";inherits:false;initial-value:normal}@property --__text-fluid{syntax:"<number>";inherits:false;initial-value:0}@property --__text-size-min{syntax:"<length>";inherits:false;initial-value:1rem}@property --__text-size-max{syntax:"<length>";inherits:false;initial-value:1rem}.viraui__text-module__Text_bS8kv{--__text-fluid:calc((100vi - 320px) / 1125px);--__text-size-min:var(--font-scale-body-s);--__text-size-max:var(--font-scale-body-m);--vui-text-font-size:clamp(
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-text-align{syntax:"*";inherits:false;initial-value:start}@property --vui-text-max-width{syntax:"*";inherits:false;initial-value:none}@property --vui-text-literal-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-text-white-space{syntax:"*";inherits:false;initial-value:normal}@property --vui-text-wrap{syntax:"*";inherits:false;initial-value:pretty}@property --vui-text-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --vui-text-text-transform{syntax:"none | capitalize | uppercase | lowercase";inherits:false;initial-value:none}@property --vui-text-font-weight{syntax:"<number>";inherits:false;initial-value:400}@property --vui-text-font-width{syntax:"<number>";inherits:false;initial-value:100}@property --vui-text-font-opsz{syntax:"<number>";inherits:false;initial-value:16}@property --vui-text-trim{syntax:"*";inherits:false;initial-value:trim-both}@property --vui-text-trim-type{syntax:"*";inherits:false;initial-value:cap alphabetic}@property --vui-text-padding-inline-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-inline-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-block-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-padding-block-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-text-font-size{syntax:"<length>";inherits:false;initial-value:1rem}@property --vui-text-font-family{syntax:"*";inherits:false;initial-value:sans-serif}@property --vui-text-line-height{syntax:"normal | <number> | <length-percentage>";inherits:false;initial-value:normal}@property --__text-fluid{syntax:"<number>";inherits:false;initial-value:0}@property --__text-size-min{syntax:"<length>";inherits:false;initial-value:1rem}@property --__text-size-max{syntax:"<length>";inherits:false;initial-value:1rem}.viraui__text-module__Text_bS8kv{--__text-fluid:calc((100vi - 320px) / 1125px);--__text-size-min:var(--font-scale-body-s);--__text-size-max:var(--font-scale-body-m);--vui-text-font-size:clamp(
|
|
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);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-
|
|
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);text-wrap:var(--vui-text-wrap);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-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-min:var(--font-scale-body-s);--__text-size-max:var(--font-scale-body-s)}&[data-text-size=xs]{--__text-size-min:var(--font-scale-body-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)}}}
|
|
@@ -85,7 +85,9 @@ export type TextProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
85
85
|
*/
|
|
86
86
|
maxWidth?: string;
|
|
87
87
|
/**
|
|
88
|
-
* Controls white-space
|
|
88
|
+
* Controls white-space collapse/preserve (`white-space`).
|
|
89
|
+
* Prefer `whiteSpace="nowrap"` for single-line metrics/chrome (also collapses spaces).
|
|
90
|
+
* For wrapping algorithm / even line lengths use `wrap` (`text-wrap`) — not this prop.
|
|
89
91
|
*
|
|
90
92
|
* @defaultValue 'normal'
|
|
91
93
|
*/
|
|
@@ -105,13 +107,14 @@ export type TextProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
105
107
|
*/
|
|
106
108
|
transform?: React.CSSProperties['textTransform'];
|
|
107
109
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
110
|
+
* Controls CSS `text-wrap` (wrapping algorithm / wrap on-off).
|
|
111
|
+
* Default `pretty` for orphan-aware UI copy. Prefer `balance` on short multi-line
|
|
112
|
+
* heads/blurbs that need even line lengths. Use `wrap="nowrap"` only when you need
|
|
113
|
+
* no wrap without changing white-space collapse; prefer `whiteSpace="nowrap"` for metrics.
|
|
111
114
|
*
|
|
112
|
-
* @defaultValue
|
|
115
|
+
* @defaultValue 'pretty'
|
|
113
116
|
*/
|
|
114
|
-
|
|
117
|
+
wrap?: React.CSSProperties['textWrap'];
|
|
115
118
|
/**
|
|
116
119
|
* Enables text selection on the rendered copy.
|
|
117
120
|
*
|
|
@@ -4,7 +4,7 @@ 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,
|
|
7
|
+
var Text = ({ align, 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", wrap = "pretty", ...otherProps }) => {
|
|
8
8
|
const hasAlign = align !== void 0;
|
|
9
9
|
const hasPadding = Boolean(hPadding || vPadding);
|
|
10
10
|
const dynamicStyle = {
|
|
@@ -12,6 +12,7 @@ var Text = ({ align, balanced = false, selection = true, className, color, famil
|
|
|
12
12
|
"--vui-text-max-width": maxWidth ?? "none",
|
|
13
13
|
"--vui-text-literal-color": color,
|
|
14
14
|
"--vui-text-white-space": whiteSpace,
|
|
15
|
+
"--vui-text-wrap": wrap,
|
|
15
16
|
"--vui-text-letter-spacing": tracking,
|
|
16
17
|
"--vui-text-text-transform": transform,
|
|
17
18
|
"--vui-text-trim": trim ? `trim-${trim}` : "none",
|
|
@@ -34,7 +35,6 @@ var Text = ({ align, balanced = false, selection = true, className, color, famil
|
|
|
34
35
|
"data-text-weight": typeof weight === "string" ? weight : "custom",
|
|
35
36
|
"data-text-tone": tone,
|
|
36
37
|
"data-text-line-height": lineHeight,
|
|
37
|
-
"data-text-balanced": balanced ? "true" : "false",
|
|
38
38
|
"data-text-selection": selection ? "true" : "false",
|
|
39
39
|
"data-text-fluid": fluid ? "true" : "false",
|
|
40
40
|
"data-text-has-align": hasAlign ? "true" : "false",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-title-align{syntax:"*";inherits:false;initial-value:start}@property --vui-title-max-width{syntax:"*";inherits:false;initial-value:none}@property --vui-title-literal-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-title-white-space{syntax:"*";inherits:false;initial-value:normal}@property --vui-title-font-weight{syntax:"<number>";inherits:false;initial-value:500}@property --vui-title-font-width{syntax:"<number>";inherits:false;initial-value:100}@property --vui-title-font-opsz{syntax:"<number>";inherits:false;initial-value:16}@property --vui-title-trim{syntax:"*";inherits:false;initial-value:trim-both}@property --vui-title-trim-type{syntax:"*";inherits:false;initial-value:cap alphabetic}@property --vui-title-padding-inline-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-inline-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-block-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-block-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-font-size{syntax:"<length>";inherits:false;initial-value:1rem}@property --vui-title-line-height{syntax:"normal | <number> | <length-percentage>";inherits:false;initial-value:normal}@property --vui-title-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --__title-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --vui-title-text-transform{syntax:"none | capitalize | uppercase | lowercase";inherits:false;initial-value:none}@property --vui-title-font-family{syntax:"*";inherits:false;initial-value:sans-serif}@property --__title-fluid{syntax:"<number>";inherits:false;initial-value:0}@property --__title-size-min{syntax:"<length>";inherits:false;initial-value:1rem}@property --__title-size-max{syntax:"<length>";inherits:false;initial-value:1rem}.viraui__title-module__Title_CXMUX{--__title-fluid:calc((100vi - 320px) / 1125px);--__title-size-min:var(--font-scale-h2);--__title-size-max:var(--font-scale-h1);--vui-title-font-size:clamp(
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-title-align{syntax:"*";inherits:false;initial-value:start}@property --vui-title-max-width{syntax:"*";inherits:false;initial-value:none}@property --vui-title-literal-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-title-white-space{syntax:"*";inherits:false;initial-value:normal}@property --vui-title-wrap{syntax:"*";inherits:false;initial-value:pretty}@property --vui-title-font-weight{syntax:"<number>";inherits:false;initial-value:500}@property --vui-title-font-width{syntax:"<number>";inherits:false;initial-value:100}@property --vui-title-font-opsz{syntax:"<number>";inherits:false;initial-value:16}@property --vui-title-trim{syntax:"*";inherits:false;initial-value:trim-both}@property --vui-title-trim-type{syntax:"*";inherits:false;initial-value:cap alphabetic}@property --vui-title-padding-inline-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-inline-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-block-start{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-padding-block-end{syntax:"<length>";inherits:false;initial-value:0}@property --vui-title-font-size{syntax:"<length>";inherits:false;initial-value:1rem}@property --vui-title-line-height{syntax:"normal | <number> | <length-percentage>";inherits:false;initial-value:normal}@property --vui-title-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --__title-letter-spacing{syntax:"normal | <length>";inherits:false;initial-value:normal}@property --vui-title-text-transform{syntax:"none | capitalize | uppercase | lowercase";inherits:false;initial-value:none}@property --vui-title-font-family{syntax:"*";inherits:false;initial-value:sans-serif}@property --__title-fluid{syntax:"<number>";inherits:false;initial-value:0}@property --__title-size-min{syntax:"<length>";inherits:false;initial-value:1rem}@property --__title-size-max{syntax:"<length>";inherits:false;initial-value:1rem}.viraui__title-module__Title_CXMUX{--__title-fluid:calc((100vi - 320px) / 1125px);--__title-size-min:var(--font-scale-h2);--__title-size-max:var(--font-scale-h1);--vui-title-font-size:clamp(
|
|
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);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-
|
|
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-wrap:var(--vui-title-wrap);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-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)}}}
|
|
@@ -72,7 +72,9 @@ export type TitleProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
72
72
|
*/
|
|
73
73
|
maxWidth?: string;
|
|
74
74
|
/**
|
|
75
|
-
* Controls white-space
|
|
75
|
+
* Controls white-space collapse/preserve (`white-space`).
|
|
76
|
+
* Prefer `whiteSpace="nowrap"` for single-line metrics/chrome (also collapses spaces).
|
|
77
|
+
* For wrapping algorithm / even line lengths use `wrap` (`text-wrap`) — not this prop.
|
|
76
78
|
*
|
|
77
79
|
* @defaultValue 'normal'
|
|
78
80
|
*/
|
|
@@ -94,13 +96,14 @@ export type TitleProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
94
96
|
*/
|
|
95
97
|
transform?: React.CSSProperties['textTransform'];
|
|
96
98
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
99
|
+
* Controls CSS `text-wrap` (wrapping algorithm / wrap on-off).
|
|
100
|
+
* Default `pretty` for orphan-aware titles. Prefer `balance` on short multi-line
|
|
101
|
+
* heads that need even line lengths. Use `wrap="nowrap"` only when you need no wrap
|
|
102
|
+
* without changing white-space collapse; prefer `whiteSpace="nowrap"` for metrics.
|
|
100
103
|
*
|
|
101
|
-
* @defaultValue
|
|
104
|
+
* @defaultValue 'pretty'
|
|
102
105
|
*/
|
|
103
|
-
|
|
106
|
+
wrap?: React.CSSProperties['textWrap'];
|
|
104
107
|
/**
|
|
105
108
|
* Enables text selection on the rendered copy.
|
|
106
109
|
*
|
|
@@ -4,7 +4,7 @@ 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,
|
|
7
|
+
var Title = ({ align, 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", wrap = "pretty", ...otherProps }) => {
|
|
8
8
|
const hasAlign = align !== void 0;
|
|
9
9
|
const hasPadding = Boolean(hPadding || vPadding);
|
|
10
10
|
const hasTracking = tracking !== void 0;
|
|
@@ -13,6 +13,7 @@ var Title = ({ align, balanced = false, selection = true, className, color, flui
|
|
|
13
13
|
"--vui-title-max-width": maxWidth ?? "none",
|
|
14
14
|
"--vui-title-literal-color": color,
|
|
15
15
|
"--vui-title-white-space": whiteSpace,
|
|
16
|
+
"--vui-title-wrap": wrap,
|
|
16
17
|
...hasTracking && { "--vui-title-letter-spacing": tracking },
|
|
17
18
|
"--vui-title-text-transform": transform,
|
|
18
19
|
"--vui-title-trim": trim ? `trim-${trim}` : "none",
|
|
@@ -32,7 +33,6 @@ var Title = ({ align, balanced = false, selection = true, className, color, flui
|
|
|
32
33
|
...otherProps,
|
|
33
34
|
"data-title-size": size,
|
|
34
35
|
"data-title-line-height": lineHeight,
|
|
35
|
-
"data-title-balanced": balanced ? "true" : "false",
|
|
36
36
|
"data-title-selection": selection ? "true" : "false",
|
|
37
37
|
"data-title-fluid": fluid ? "true" : "false",
|
|
38
38
|
"data-title-has-align": hasAlign ? "true" : "false",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<meta id="prose" title="Prose" category="typography" shared_contract="prose">
|
|
3
|
-
<summary>Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm
|
|
3
|
+
<summary>Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm, region `wrap` (default `pretty`), and reading column via parent Stack `maxWidth` sized for body Text `size`.</summary>
|
|
4
4
|
<exports>
|
|
5
5
|
<export name="Prose" />
|
|
6
6
|
</exports>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
</Prose>
|
|
37
37
|
</Stack>
|
|
38
38
|
]]></tsx>
|
|
39
|
-
<notes>Text-wall path: Prose for rhythm + Stack measure sized for body `size` (here `s` → narrower than `m`). Not for short card blurbs — those stay Text/Title props (`maxWidth` / `
|
|
39
|
+
<notes>Text-wall path: Prose for rhythm + Stack measure sized for body `size` (here `s` → narrower than `m`). Not for short card blurbs — those stay Text/Title props (`maxWidth` / `wrap`).</notes>
|
|
40
40
|
</example>
|
|
41
41
|
|
|
42
42
|
<example title="Density and region align">
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
<when_to_use>
|
|
4
4
|
<item>Text walls — articles, docs, privacy/terms, long editorial — whose direct children are headings and blocks that need heading-aware vertical rhythm</item>
|
|
5
5
|
<item>Article, docs, or changelog regions where Stack `rowGap` would make heading→body and body→body the same</item>
|
|
6
|
-
<item>Optional `gap` density (`s` / `m` / `l`)
|
|
6
|
+
<item>Optional `gap` density (`s` / `m` / `l`), region `align` (Title/Text that omit `align` inherit it), and region `wrap` (default `pretty`; inherits to descendants)</item>
|
|
7
7
|
<item>Reading column via parent Stack `maxWidth` sized for the body Text `size` (smaller → narrower; not one fixed `65ch`–`75ch`); edge-to-edge wall of text is not OK</item>
|
|
8
8
|
</when_to_use>
|
|
9
9
|
<when_not_to_use>
|
|
10
10
|
<item>UI chrome spacing (cards, toolbars, form rows) — use Stack `rowGap` / `columnGap`</item>
|
|
11
|
-
<item>Short card/hero blurbs “for leading” — use Text/Title `lineHeight` / `maxWidth` / `
|
|
11
|
+
<item>Short card/hero blurbs “for leading” — use Text/Title `lineHeight` / `maxWidth` / `wrap` instead</item>
|
|
12
12
|
<item>Restyling type, lists, links, or tables — stay on Title / Text / consumer CSS</item>
|
|
13
13
|
<item>Markdown parse or sanitize — Prose only spaces matching direct children</item>
|
|
14
14
|
</when_not_to_use>
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
<prop name="gap" kind="react">
|
|
19
19
|
<description>Em density for heading-aware vertical rhythm (`s` < `m` < `l`).</description>
|
|
20
20
|
</prop>
|
|
21
|
+
<prop name="wrap" kind="react">
|
|
22
|
+
<description>Controls CSS `text-wrap` on the Prose root (inherits to descendants unless a child overrides). Default `pretty` for orphan-aware article walls. Prefer `balance` only on short multi-line blocks that need even line lengths. Single-line metrics stay on child Text/Title `whiteSpace`.</description>
|
|
23
|
+
</prop>
|
|
21
24
|
<prop name="ref" kind="react">
|
|
22
25
|
<description>Ref forwarded to the rendered prose root element.</description>
|
|
23
26
|
</prop>
|
|
@@ -36,4 +39,7 @@
|
|
|
36
39
|
<prop name="gap-heading-start" kind="css">
|
|
37
40
|
<description>Gap after a heading onto the next sibling (`--vui-prose-gap-heading-start`). Default `1.3em` (`l`).</description>
|
|
38
41
|
</prop>
|
|
42
|
+
<prop name="wrap" kind="css">
|
|
43
|
+
<description>Text-wrap on the Prose root (`--vui-prose-wrap`; inherits). Prefer the `wrap` React prop; default `pretty`.</description>
|
|
44
|
+
</prop>
|
|
39
45
|
</props>
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
<do>Keep a reading column: wrap Prose in Stack `maxWidth` in **`ch`** sized for the **body Text `size`** (smaller type → narrower; `m` mid band; large body → wider — not one fixed band for every article); prefer `ch` over rem; edge-to-edge wall of text is not OK</do>
|
|
7
7
|
<do>Use `render={<article />}` (or another landmark) when the region is the document landmark; name it if the landmark needs a name</do>
|
|
8
8
|
<do>Leave type on Title/Text; Title inside Prose uses `lineHeight="s"`; body leading via Text `lineHeight` per block (`m` / `l` / `s`); override `--vui-prose-gap-*` on the Prose root only when the em triple must change</do>
|
|
9
|
+
<do>Default Prose `wrap` is `pretty` (inherits to children). Prefer `wrap="balance"` only when short multi-line blocks in the region need even line lengths; single-line metrics use child Text/Title `whiteSpace="nowrap"`</do>
|
|
9
10
|
<dont>Using Stack `rowGap` as article rhythm — one gap cannot split heading→body vs body→body</dont>
|
|
10
11
|
<dont>Expecting default Title/Text `span` hosts to join rhythm — they are not heading/block tags</dont>
|
|
11
12
|
<dont>Setting `as` — replace the root with `render` only</dont>
|
|
12
13
|
<dont>Setting `align` on every Title/Text inside Prose when one Prose `align` covers the region</dont>
|
|
13
14
|
<dont>Setting a widget `role` on Prose, or painting font/color/list/link chrome in Prose CSS</dont>
|
|
14
|
-
<dont>Wrapping a short UI blurb in Prose “for leading” — use Text/Title `lineHeight` / `maxWidth` / `
|
|
15
|
+
<dont>Wrapping a short UI blurb in Prose “for leading” — use Text/Title `lineHeight` / `maxWidth` / `wrap` instead</dont>
|
|
15
16
|
</usage>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<meta id="text" title="Text" category="typography" shared_contract="text">
|
|
3
|
-
<summary>Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `
|
|
3
|
+
<summary>Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `wrap`).</summary>
|
|
4
4
|
<exports>
|
|
5
5
|
<export name="Text" />
|
|
6
6
|
</exports>
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
<example title="Auth muted lead (readable measure)">
|
|
7
7
|
<tsx><![CDATA[
|
|
8
8
|
<Stack vPadding={[0, 'l']} rowGap="xl" maxWidth="70ch">
|
|
9
|
-
<Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s"
|
|
9
|
+
<Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s" wrap="balance">
|
|
10
10
|
Design systems that ship
|
|
11
11
|
</Title>
|
|
12
|
-
<Text size="2xl" tone="muted" maxWidth="68ch"
|
|
12
|
+
<Text size="2xl" tone="muted" maxWidth="68ch" wrap="balance">
|
|
13
13
|
Login to create custom ViraUI themes, access prompts and more.
|
|
14
14
|
</Text>
|
|
15
15
|
</Stack>
|
|
16
16
|
]]></tsx>
|
|
17
|
-
<notes>From `auth/login-view`. Cap ~10–12 words/line with `maxWidth` in `ch` scaled to size (`2xl` wider than `s`); `
|
|
17
|
+
<notes>From `auth/login-view`. Cap ~10–12 words/line with `maxWidth` in `ch` scaled to size (`2xl` wider than `s`); `wrap="balance"` on short even-rag hero copy (default `pretty` covers most body); `lineHeight="s"` on large Title. Prefer `ch` over rem. No CSS width/line-height.</notes>
|
|
18
18
|
</example>
|
|
19
19
|
|
|
20
20
|
<example title="Card section blurb">
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
<Title size="6" render={<h2 />}>
|
|
24
24
|
Payout Threshold
|
|
25
25
|
</Title>
|
|
26
|
-
<Text tone="muted" size="s" maxWidth="45ch"
|
|
26
|
+
<Text tone="muted" size="s" maxWidth="45ch">
|
|
27
27
|
Set the minimum balance required before a payout is triggered.
|
|
28
28
|
</Text>
|
|
29
29
|
</Stack>
|
|
30
30
|
]]></tsx>
|
|
31
|
-
<notes>From preview-grid `payout-threshold` (+ preferences / workspace-usage). Default pairing: Title + muted `size="s"` Text; short blurb prefers `maxWidth` in `ch`
|
|
31
|
+
<notes>From preview-grid `payout-threshold` (+ preferences / workspace-usage). Default pairing: Title + muted `size="s"` Text; short blurb prefers `maxWidth` in `ch` (default `wrap="pretty"`).</notes>
|
|
32
32
|
</example>
|
|
33
33
|
|
|
34
34
|
<example title="Nested weight + tone">
|
|
@@ -52,16 +52,16 @@
|
|
|
52
52
|
</Text>
|
|
53
53
|
</Stack>
|
|
54
54
|
]]></tsx>
|
|
55
|
-
<notes>From preview-grid `payout-threshold`. `family="mono"` + muted xs for metric chrome — single-line;
|
|
55
|
+
<notes>From preview-grid `payout-threshold`. `family="mono"` + muted xs for metric chrome — single-line; prefer `whiteSpace="nowrap"` when overflow risk; do not use `wrap="nowrap"` for metrics.</notes>
|
|
56
56
|
</example>
|
|
57
57
|
|
|
58
58
|
<example title="Form success paragraph">
|
|
59
59
|
<tsx><![CDATA[
|
|
60
|
-
<Text render={<p />} maxWidth="60ch"
|
|
60
|
+
<Text render={<p />} maxWidth="60ch">
|
|
61
61
|
Check your email — we sent a link to your@email.com
|
|
62
62
|
</Text>
|
|
63
63
|
]]></tsx>
|
|
64
|
-
<notes>From `auth/login-form` acknowledged state. `render={<p />}` for block semantics; short wrap
|
|
64
|
+
<notes>From `auth/login-form` acknowledged state. `render={<p />}` for block semantics; short wrap relies on default `pretty` + `ch` measure.</notes>
|
|
65
65
|
</example>
|
|
66
66
|
|
|
67
67
|
<example title="Prose link">
|
|
@@ -76,15 +76,15 @@
|
|
|
76
76
|
<example title="Empty-state centered">
|
|
77
77
|
<tsx><![CDATA[
|
|
78
78
|
<Stack hAlign="center" rowGap="m" maxWidth="36ch">
|
|
79
|
-
<Title align="center" size="5" render={<h3 />}
|
|
79
|
+
<Title align="center" size="5" render={<h3 />} wrap="balance">
|
|
80
80
|
No Team Members
|
|
81
81
|
</Title>
|
|
82
|
-
<Text size="s" align="center" maxWidth="40ch" tone="muted"
|
|
82
|
+
<Text size="s" align="center" maxWidth="40ch" tone="muted" wrap="balance">
|
|
83
83
|
Invite your team to collaborate on this project.
|
|
84
84
|
</Text>
|
|
85
85
|
</Stack>
|
|
86
86
|
]]></tsx>
|
|
87
|
-
<notes>From preview-grid `empty-team`. Centered muted copy with `maxWidth` in `ch` + `
|
|
87
|
+
<notes>From preview-grid `empty-team`. Centered muted copy with `maxWidth` in `ch` + `wrap="balance"` for even rag; Stack owns column cap / centering.</notes>
|
|
88
88
|
</example>
|
|
89
89
|
|
|
90
90
|
<example title="Claimable balance rows">
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<props surface="text">
|
|
3
3
|
<when_to_use>
|
|
4
4
|
<item>Body copy with semantic tone (including brand `primary`) and token-backed typography</item>
|
|
5
|
-
<item>Readable UI measure (~10–12 words/line) via `maxWidth` / parent Stack `maxWidth` **scaled to `size`** (smaller → narrower), `lineHeight` matched to size, and `
|
|
5
|
+
<item>Readable UI measure (~10–12 words/line) via `maxWidth` / parent Stack `maxWidth` **scaled to `size`** (smaller → narrower), `lineHeight` matched to size, and `wrap` (default `pretty`; `balance` on short even-rag blurbs)</item>
|
|
6
6
|
<item>Letter-spacing via `tracking` or text-transform via `transform` — prefer props over CSS on the Text root</item>
|
|
7
7
|
<item>Helper, label, or inline copy inside other components</item>
|
|
8
8
|
<item>Mono or body family text; size locked by default (`fluid` false → --font-scale-body-*); pass `fluid` for viewport clamp (also gated by theme `--effect-fluid-typography`)</item>
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
<item>Headings or display typography — use Title</item>
|
|
12
12
|
<item>Heading semantics without an appropriate render root</item>
|
|
13
13
|
<item>Long-form heading-aware article rhythm — use Prose (keep a reading column)</item>
|
|
14
|
-
<item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap
|
|
14
|
+
<item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap` / `width: 100%` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `wrap` / parent Stack `fullWidth` cover the need</item>
|
|
15
15
|
</when_not_to_use>
|
|
16
16
|
<prop name="align" kind="react">
|
|
17
17
|
<description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
|
|
18
18
|
</prop>
|
|
19
|
-
<prop name="balanced" kind="react">
|
|
20
|
-
<description>Enables balanced wrapping for block-like copy. Prefer on short wrapping UI paragraphs (hero lead, empty state, card blurb); omit when single-line or intentional ragged.</description>
|
|
21
|
-
</prop>
|
|
22
19
|
<prop name="color" kind="react">
|
|
23
20
|
<description>Applies a literal color escape hatch when semantic `tone` is not used.</description>
|
|
24
21
|
</prop>
|
|
@@ -77,7 +74,10 @@
|
|
|
77
74
|
<description>Selects a semantic weight keyword or a numeric weight (`font-weight` + `wght` axis).</description>
|
|
78
75
|
</prop>
|
|
79
76
|
<prop name="whiteSpace" kind="react">
|
|
80
|
-
<description>Controls white-space
|
|
77
|
+
<description>Controls white-space collapse/preserve (`white-space`). Prefer `whiteSpace="nowrap"` for single-line metrics/chrome (also collapses spaces). For wrapping algorithm / even line lengths use `wrap` (`text-wrap`) — not this prop.</description>
|
|
78
|
+
</prop>
|
|
79
|
+
<prop name="wrap" kind="react">
|
|
80
|
+
<description>Controls CSS `text-wrap` (wrapping algorithm / wrap on-off). Default `pretty` for orphan-aware UI copy. Prefer `balance` on short multi-line heads/blurbs that need even line lengths. Use `wrap="nowrap"` only when you need no wrap without changing white-space collapse; prefer `whiteSpace="nowrap"` for metrics.</description>
|
|
81
81
|
</prop>
|
|
82
82
|
<prop name="align" kind="css">
|
|
83
83
|
<description>Text alignment on the root when the `align` prop is set; omitted inherits parent `text-align`.</description>
|
|
@@ -131,6 +131,9 @@
|
|
|
131
131
|
<description>Text transform on the root. Prefer the `transform` React prop; set this hook only when theming beyond the prop.</description>
|
|
132
132
|
</prop>
|
|
133
133
|
<prop name="white-space" kind="css">
|
|
134
|
-
<description>White-space behavior on the root.</description>
|
|
134
|
+
<description>White-space behavior on the root (`--vui-text-white-space`). Prefer the `whiteSpace` React prop.</description>
|
|
135
|
+
</prop>
|
|
136
|
+
<prop name="wrap" kind="css">
|
|
137
|
+
<description>Text-wrap on the root (`--vui-text-wrap`). Prefer the `wrap` React prop; default `pretty`.</description>
|
|
135
138
|
</prop>
|
|
136
139
|
</props>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<do>Body copy, helpers, and labels — semantic `tone` before literal `color` or raw theme vars</do>
|
|
4
4
|
<do>Readable UI measure: aim ~10–12 words per line — `maxWidth` / Stack `maxWidth` **scales with `size`** (smaller type → narrower column; larger → wider); prefer **`ch`** in patterns/examples (not `rem`); never one fixed ch for every size; no full-bleed wrapping blurbs</do>
|
|
5
5
|
<do>Match `lineHeight` to size/context (`m` default on root; `l` when open; `s` for dense chrome); omit `lineHeight`/`size` on nested Text so the outer Text owns type</do>
|
|
6
|
-
<do>
|
|
6
|
+
<do>Default `wrap` is `pretty` (orphan-aware). Prefer `wrap="balance"` on short multi-line blurbs that need even line lengths; use `whiteSpace="nowrap"` for single-line metrics (not `wrap="nowrap"` unless you must keep space collapse unchanged)</do>
|
|
7
7
|
<do>Omit `align` so copy inherits parent `text-align`; pass `align` only to override</do>
|
|
8
8
|
<do>Card / settings section blurb: Stack `rowGap` + Title heading + muted `size="s"` Text — no invent CSS gap</do>
|
|
9
9
|
<do>Settings row: Stack `direction="row"` + Text label + Switch/Checkbox (or nest Text inside Switch `label`) — Surface for chrome</do>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<do>Long articles / docs / privacy walls → Prose (not Text alone for heading-aware rhythm); still keep a reading column</do>
|
|
17
17
|
<dont>Using Text for headings — use Title (`render` for h1–h6)</dont>
|
|
18
18
|
<dont>Putting `data-no-link-style` on Text-as-a when the default underline hover is wanted</dont>
|
|
19
|
-
<dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap
|
|
19
|
+
<dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap` / `width: 100%` / `paddingBlock*` / `paddingInline*` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `wrap` / Stack `fullWidth` / `hPadding`/`vPadding` tuples cover it</dont>
|
|
20
20
|
<dont>Setting body color with raw `--global-*` when `tone` covers the role</dont>
|
|
21
21
|
<dont>Full-bleed marketing/UI paragraphs across a wide shell when a ~10–12-word measure fits</dont>
|
|
22
22
|
<dont>Reusing a large-body shell (`~65ch`–`75ch` / wide rem) for small `size` copy — column width must track `size`</dont>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<meta id="title" title="Title" category="typography" shared_contract="title">
|
|
3
|
-
<summary>Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `
|
|
3
|
+
<summary>Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `wrap`).</summary>
|
|
4
4
|
<exports>
|
|
5
5
|
<export name="Title" />
|
|
6
6
|
</exports>
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
<example title="Auth hero heading (readable measure)">
|
|
7
7
|
<tsx><![CDATA[
|
|
8
|
-
<Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s"
|
|
8
|
+
<Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s" wrap="balance">
|
|
9
9
|
Ship real UI at the speed of thought.
|
|
10
10
|
</Title>
|
|
11
11
|
]]></tsx>
|
|
12
|
-
<notes>From `auth/login-view`. Pair `size` with `render={<h1 />}`; cap measure in `ch`; `lineHeight="s"` + `
|
|
12
|
+
<notes>From `auth/login-view`. Pair `size` with `render={<h1 />}`; cap measure in `ch`; `lineHeight="s"` + `wrap="balance"` on short even-rag display (default `pretty` covers most titles). Prefer `ch` over rem. (Live app nests Typewriter inside; see typewriter patterns.)</notes>
|
|
13
13
|
</example>
|
|
14
14
|
|
|
15
15
|
<example title="Card section heading">
|
|
@@ -18,21 +18,21 @@
|
|
|
18
18
|
<Title size="6" render={<h2 />}>
|
|
19
19
|
Preferences
|
|
20
20
|
</Title>
|
|
21
|
-
<Text size="s" maxWidth="45ch" tone="muted"
|
|
21
|
+
<Text size="s" maxWidth="45ch" tone="muted">
|
|
22
22
|
Manage your account settings and notifications.
|
|
23
23
|
</Text>
|
|
24
24
|
</Stack>
|
|
25
25
|
]]></tsx>
|
|
26
|
-
<notes>From preview-grid `preferences` (+ payout-threshold, notifications, …). Dense card titles use `size="6"` + `h2`; blurb keeps `ch` measure
|
|
26
|
+
<notes>From preview-grid `preferences` (+ payout-threshold, notifications, …). Dense card titles use `size="6"` + `h2`; blurb keeps `ch` measure (default `wrap="pretty"`).</notes>
|
|
27
27
|
</example>
|
|
28
28
|
|
|
29
29
|
<example title="Empty-state heading">
|
|
30
30
|
<tsx><![CDATA[
|
|
31
|
-
<Title align="center" size="5" render={<h3 />}
|
|
31
|
+
<Title align="center" size="5" render={<h3 />} wrap="balance">
|
|
32
32
|
No Team Members
|
|
33
33
|
</Title>
|
|
34
34
|
]]></tsx>
|
|
35
|
-
<notes>From preview-grid `empty-team`. Centered mid-size heading; prefer `
|
|
35
|
+
<notes>From preview-grid `empty-team`. Centered mid-size heading; prefer `wrap="balance"` when the title wraps and needs even rag; pair with muted Text below.</notes>
|
|
36
36
|
</example>
|
|
37
37
|
|
|
38
38
|
<example title="Display metric">
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
</Text>
|
|
47
47
|
</Stack>
|
|
48
48
|
]]></tsx>
|
|
49
|
-
<notes>From preview-grid `daily-activity-goal`. `size="display"` for hero numerals; `whiteSpace="nowrap"` keeps metric on one line —
|
|
49
|
+
<notes>From preview-grid `daily-activity-goal`. `size="display"` for hero numerals; `whiteSpace="nowrap"` keeps metric on one line — prefer that over `wrap="nowrap"`.</notes>
|
|
50
50
|
</example>
|
|
51
51
|
|
|
52
52
|
<example title="Balance display">
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<Title size="4" render={<h3 />}>
|
|
70
70
|
Colors
|
|
71
71
|
</Title>
|
|
72
|
-
<Text size="s" tone="muted" maxWidth="48ch"
|
|
72
|
+
<Text size="s" tone="muted" maxWidth="48ch">
|
|
73
73
|
Preview of theme voice, contrast, and color rhythm across accents.
|
|
74
74
|
</Text>
|
|
75
75
|
</Stack>
|
|
@@ -4,21 +4,18 @@
|
|
|
4
4
|
<item>Headings and display text with title presets</item>
|
|
5
5
|
<item>Semantic heading tags chosen explicitly through render</item>
|
|
6
6
|
<item>Size and render paired together for most heading use cases</item>
|
|
7
|
-
<item>Readable wrapping titles: `maxWidth` (~10–12 words/line, **scaled to `size`** — smaller → narrower), `lineHeight` (`s` on large wrapping / inside Prose), and `
|
|
7
|
+
<item>Readable wrapping titles: `maxWidth` (~10–12 words/line, **scaled to `size`** — smaller → narrower), `lineHeight` (`s` on large wrapping / inside Prose), and `wrap` (default `pretty`; `balance` on short even-rag heads)</item>
|
|
8
8
|
<item>Letter-spacing via `tracking` or text-transform via `transform` — prefer props over CSS on the Title root</item>
|
|
9
9
|
<item>Fluid clamp sizing across title appearance presets (theme `--effect-fluid-typography` or per-instance `fluid`; off → fixed at --font-scale-display / h-* for that size)</item>
|
|
10
10
|
</when_to_use>
|
|
11
11
|
<when_not_to_use>
|
|
12
12
|
<item>Body copy — use Text</item>
|
|
13
13
|
<item>Body or mono family typography</item>
|
|
14
|
-
<item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap
|
|
14
|
+
<item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap` / `width: 100%` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `wrap` / parent Stack `fullWidth` cover the need</item>
|
|
15
15
|
</when_not_to_use>
|
|
16
16
|
<prop name="align" kind="react">
|
|
17
17
|
<description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
|
|
18
18
|
</prop>
|
|
19
|
-
<prop name="balanced" kind="react">
|
|
20
|
-
<description>Enables balanced wrapping for block-like titles. Prefer on short wrapping heads (hero, empty state, section); omit for single-line / `whiteSpace="nowrap"` metrics.</description>
|
|
21
|
-
</prop>
|
|
22
19
|
<prop name="color" kind="react">
|
|
23
20
|
<description>Applies a literal color escape hatch.</description>
|
|
24
21
|
</prop>
|
|
@@ -71,7 +68,10 @@
|
|
|
71
68
|
<description>Overrides the size preset weight (`font-weight` + `wght` axis).</description>
|
|
72
69
|
</prop>
|
|
73
70
|
<prop name="whiteSpace" kind="react">
|
|
74
|
-
<description>Controls white-space
|
|
71
|
+
<description>Controls white-space collapse/preserve (`white-space`). Prefer `whiteSpace="nowrap"` for single-line metrics/chrome (also collapses spaces). For wrapping algorithm / even line lengths use `wrap` (`text-wrap`) — not this prop.</description>
|
|
72
|
+
</prop>
|
|
73
|
+
<prop name="wrap" kind="react">
|
|
74
|
+
<description>Controls CSS `text-wrap` (wrapping algorithm / wrap on-off). Default `pretty` for orphan-aware titles. Prefer `balance` on short multi-line heads that need even line lengths. Use `wrap="nowrap"` only when you need no wrap without changing white-space collapse; prefer `whiteSpace="nowrap"` for metrics.</description>
|
|
75
75
|
</prop>
|
|
76
76
|
<prop name="align" kind="css">
|
|
77
77
|
<description>Title alignment on the root when the `align` prop is set; omitted inherits parent `text-align`.</description>
|
|
@@ -125,6 +125,9 @@
|
|
|
125
125
|
<description>Text transform on the root. Prefer the `transform` React prop; set this hook only when theming beyond the prop.</description>
|
|
126
126
|
</prop>
|
|
127
127
|
<prop name="white-space" kind="css">
|
|
128
|
-
<description>White-space behavior on the root.</description>
|
|
128
|
+
<description>White-space behavior on the root (`--vui-title-white-space`). Prefer the `whiteSpace` React prop.</description>
|
|
129
|
+
</prop>
|
|
130
|
+
<prop name="wrap" kind="css">
|
|
131
|
+
<description>Text-wrap on the root (`--vui-title-wrap`). Prefer the `wrap` React prop; default `pretty`.</description>
|
|
129
132
|
</prop>
|
|
130
133
|
</props>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<usage surface="title">
|
|
3
3
|
<do>Headings and display type — pair `size` (visual preset) with `render={<h1 />}`…`h6` for outline semantics (default root is `span`)</do>
|
|
4
4
|
<do>Readable measure on wrapping titles: aim ~10–12 words per line — `maxWidth` / Stack `maxWidth` **scales with `size`** (smaller → narrower; larger → wider); prefer **`ch`** (not `rem`) in patterns/examples; large wrapping presets often `lineHeight="s"`</do>
|
|
5
|
-
<do>
|
|
5
|
+
<do>Default `wrap` is `pretty`. Prefer `wrap="balance"` on short multi-line heads that need even line lengths; use `whiteSpace="nowrap"` for single-line metrics (not `wrap="nowrap"` unless space collapse must stay unchanged)</do>
|
|
6
6
|
<do>Omit `align` so the heading inherits parent `text-align`; pass `align` only to override</do>
|
|
7
7
|
<do>Title inside Prose → `lineHeight="s"`; long-form rhythm stays on Prose — not Stack `rowGap`</do>
|
|
8
8
|
<do>Card / panel header: Stack `rowGap="xs"` + Title + muted Text blurb — Surface for card chrome</do>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<do>Title-as-prose-link (`render={<a />}`) keeps preflight link chrome — omit `data-no-link-style`</do>
|
|
15
15
|
<dont>Assuming `size` changes the HTML tag — choose `render` explicitly</dont>
|
|
16
16
|
<dont>Using Title for long body paragraphs — use Text</dont>
|
|
17
|
-
<dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap
|
|
18
|
-
<dont>Full-bleed wrapping headlines across a wide shell when a capped measure + `
|
|
17
|
+
<dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap` / `width: 100%` / `paddingBlock*` / `paddingInline*` when props / Stack / `hPadding`/`vPadding` tuples cover it</dont>
|
|
18
|
+
<dont>Full-bleed wrapping headlines across a wide shell when a capped measure + `wrap` fits</dont>
|
|
19
19
|
<dont>One fixed wide measure for every Title `size` — smaller presets need a narrower column for ~10–12 words/line</dont>
|
|
20
20
|
</usage>
|
package/dist/specs/index.xml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<index package="@viraui/react" version="2026.9.
|
|
2
|
+
<index package="@viraui/react" version="2026.9.1" generated="2026-10-15T08:18:55.911Z">
|
|
3
3
|
<categories>
|
|
4
4
|
<category id="layout" />
|
|
5
5
|
<category id="action" />
|
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
</units>
|
|
313
313
|
</entry>
|
|
314
314
|
<entry id="prose" category="typography" title="Prose">
|
|
315
|
-
<summary>Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm
|
|
315
|
+
<summary>Text-wall grouping region (articles, docs, privacy) with heading-aware em rhythm, region `wrap` (default `pretty`), and reading column via parent Stack `maxWidth` sized for body Text `size`.</summary>
|
|
316
316
|
<units>
|
|
317
317
|
<unit kind="meta" href="components/prose/meta.xml" />
|
|
318
318
|
<unit kind="a11y" href="components/prose/a11y.xml" />
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
</units>
|
|
443
443
|
</entry>
|
|
444
444
|
<entry id="text" category="typography" title="Text">
|
|
445
|
-
<summary>Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `
|
|
445
|
+
<summary>Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `wrap`).</summary>
|
|
446
446
|
<units>
|
|
447
447
|
<unit kind="meta" href="components/text/meta.xml" />
|
|
448
448
|
<unit kind="a11y" href="components/text/a11y.xml" />
|
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
</units>
|
|
473
473
|
</entry>
|
|
474
474
|
<entry id="title" category="typography" title="Title">
|
|
475
|
-
<summary>Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `
|
|
475
|
+
<summary>Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `wrap`).</summary>
|
|
476
476
|
<units>
|
|
477
477
|
<unit kind="meta" href="components/title/meta.xml" />
|
|
478
478
|
<unit kind="a11y" href="components/title/a11y.xml" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viraui/react",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@base-ui/react": ">=1.8.0",
|
|
31
|
-
"@viraui/foundation": "2026.9.
|
|
31
|
+
"@viraui/foundation": "2026.9.1",
|
|
32
32
|
"@viraui/icons": "2026.1.0",
|
|
33
33
|
"@viraui/postcss-config": "2026.0.0",
|
|
34
34
|
"@viraui/tsconfig": "2026.0.0",
|