@kanso-labs/kanso-ui 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -74,8 +74,42 @@ belongs to whatever holds both of them. For the page-level layouts — a list
74
74
  beside a detail pane, a main pane with a companion — reach for `ListDetail` and
75
75
  `SupportingPane` instead.
76
76
 
77
+ An `AppBar` that paints edge to edge can still line its contents up with the
78
+ page beneath it. Give it the page's measure and the page's gutter:
79
+
80
+ ```tsx
81
+ <AppBar contentInset="24px" contentMaxInlineSize="960px" headline="Headline" />
82
+ <Container maxInlineSize="960px">{page}</Container>
83
+ ```
84
+
85
+ `AppBar` takes `scrolled` and `collapsed`, and both are controlled — only the
86
+ app knows which element scrolls. A pinned flexible bar gives its height back as
87
+ the page scrolls, becoming the small bar:
88
+
89
+ ```tsx
90
+ <AppBar
91
+ collapsed={scrollTop > 24}
92
+ contentMaxInlineSize="960px"
93
+ headline="Headline"
94
+ scrolled={scrollTop > 0}
95
+ size="large"
96
+ />
97
+ ```
98
+
77
99
  ### Rendering as a different element
78
100
 
101
+ `Text` renders a `<span>`, which is right for a run of text inside a line and
102
+ wrong for a paragraph. `block` renders a `<p>` instead, so multi-sentence copy
103
+ needs no element named at the call site:
104
+
105
+ ```tsx
106
+ <Text block>First sentence of the copy.</Text>
107
+ ```
108
+
109
+ It carries no margin, the same as every other `Text`, so the space between two
110
+ paragraphs is a decision the container makes rather than one the browser makes
111
+ for it.
112
+
79
113
  `render` swaps the element a component produces, keeping its styling:
80
114
 
81
115
  ```tsx
@@ -8,6 +8,51 @@ type AppBarProps = Omit<useRender.ComponentProps<'header'>, 'children'> & {
8
8
  * @default 'start'
9
9
  */
10
10
  align?: 'center' | 'start';
11
+ /**
12
+ * Collapses a flexible bar to the height and headline of the small one, for
13
+ * a pinned bar over content that has been scrolled. M3 specifies it for the
14
+ * flexible sizes, and without it a pinned `large` costs 152px of the
15
+ * viewport for as long as the page is open.
16
+ *
17
+ * Controlled, and for the same reason `scrolled` is: only the app knows
18
+ * which element scrolls, and how far into it the bar should have finished
19
+ * collapsing. Usually set from the same handler.
20
+ *
21
+ * Ignored on `small`, which is already the height this collapses to. The
22
+ * subtitle goes with the height, since there is no room for a second line
23
+ * in the small bar.
24
+ *
25
+ * Collapsing hands the page back the height the bar gives up, which
26
+ * shortens the scroll range. Where the content is barely longer than the
27
+ * viewport that can pull the position back under whatever threshold set
28
+ * this, expanding the bar again — so pick a threshold with room under it,
29
+ * or drive this from something other than a raw offset.
30
+ * @default false
31
+ */
32
+ collapsed?: boolean;
33
+ /**
34
+ * Where the bar's content starts, measured to the headline. A leading slot
35
+ * sits 12px before that, which is the room an icon button's own padding
36
+ * fills around its glyph — M3's arrangement, and what makes a bar with an
37
+ * icon and one without start their text in the same place.
38
+ *
39
+ * The default is M3's own margin for a top app bar, which is also the
40
+ * margin it gives body content, so a bar and the page beneath line up
41
+ * without either being told about the other. Set it to the page's gutter
42
+ * where that differs.
43
+ * @default '16px'
44
+ */
45
+ contentInset?: string;
46
+ /**
47
+ * How wide the bar's contents may run before they stop growing, with the
48
+ * row centred in whatever is left. The bar's own surface still paints edge
49
+ * to edge.
50
+ *
51
+ * This is what lines a full-bleed bar up with a page whose content sits in
52
+ * a measure: give it the page's measure, and `contentInset` the page's
53
+ * gutter. Unset, the row is as wide as the bar.
54
+ */
55
+ contentMaxInlineSize?: string;
11
56
  /** The page's title. Wraps rather than truncating, and grows the bar with it. */
12
57
  headline?: ReactNode;
13
58
  /** Usually a back or menu icon button. Sits before the text. */
@@ -45,9 +90,10 @@ type AppBarProps = Omit<useRender.ComponentProps<'header'>, 'children'> & {
45
90
  *
46
91
  * It paints its own surface, unlike the layout components, because separating
47
92
  * itself from the content beneath is the job — which is also why `scrolled`
48
- * exists.
93
+ * and `collapsed` exist. Neither watches the page: only the app knows which
94
+ * element scrolls, so both are set from its own scroll handler.
49
95
  */
50
- declare function AppBar({ align, headline, leading, render, scrolled, size, subtitle, trailing, ...props }: AppBarProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
96
+ declare function AppBar({ align, collapsed, contentInset, contentMaxInlineSize, headline, leading, render, scrolled, size, subtitle, trailing, ...props }: AppBarProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
51
97
  //#endregion
52
98
  export { type AppBarProps, AppBar as default };
53
99
  //# sourceMappingURL=index.d.ts.map
@@ -2,8 +2,10 @@ import { props } from "../../node_modules/@stylexjs/stylex/lib/es/stylex.js";
2
2
  import { mergeStyles } from "../../styles/merge.js";
3
3
  import Text from "../text/index.js";
4
4
  import { useRender } from "@base-ui/react/use-render";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/app-bar/index.tsx
7
+ const HEADLINE_OFFSET = "12px";
8
+ const DEFAULT_CONTENT_INSET = "16px";
7
9
  const HEIGHTS = {
8
10
  large: {
9
11
  plain: "120px",
@@ -18,16 +20,26 @@ const HEIGHTS = {
18
20
  withSubtitle: "64px"
19
21
  }
20
22
  };
23
+ const COLLAPSED_HEIGHT = HEIGHTS.small.plain;
21
24
  const styles = {
22
25
  root: {
23
26
  kGNEyG: "x6s0dn4",
24
27
  kWkggS: "x14s9m32",
25
28
  kB7OPa: "x9f619",
26
29
  k1xSpc: "x78zum5",
30
+ kjj79g: "xl56j7k",
31
+ kIyJzY: "xioksi3",
32
+ k1ekBW: "x8u3r54",
33
+ kAMwcw: "x1seskgd",
34
+ $$css: true
35
+ },
36
+ row: {
37
+ kGNEyG: "x6s0dn4",
38
+ kB7OPa: "x9f619",
39
+ k1xSpc: "x78zum5",
27
40
  kOIVth: "xzoy561",
28
- kg3NbH: "ximhkdq",
29
- kIyJzY: "xx6bhzk",
30
- k1ekBW: "x15406qy",
41
+ kzqmXN: "xh8yej3",
42
+ kUOVxO: "xvueqy4",
31
43
  $$css: true
32
44
  },
33
45
  scrolled: {
@@ -47,6 +59,15 @@ const _temp = {
47
59
  "$$css": true
48
60
  };
49
61
  const heightStyles = { root: (minBlockSize) => [_temp, { "--x-minBlockSize": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(minBlockSize) }] };
62
+ const _temp2 = {
63
+ ks0D6T: "x13i5qev",
64
+ kg3NbH: "x30g6up",
65
+ "$$css": true
66
+ };
67
+ const rowStyles = { root: (contentInset, maxInlineSize) => [_temp2, {
68
+ "--x-maxInlineSize": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(maxInlineSize),
69
+ "--x-paddingInline": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(`calc(${contentInset} - ${HEADLINE_OFFSET})`)
70
+ }] };
50
71
  /**
51
72
  * Material 3's app bar: the container at the top of a page carrying its title,
52
73
  * one or two actions, and the way back out.
@@ -57,40 +78,47 @@ const heightStyles = { root: (minBlockSize) => [_temp, { "--x-minBlockSize": ((v
57
78
  *
58
79
  * It paints its own surface, unlike the layout components, because separating
59
80
  * itself from the content beneath is the job — which is also why `scrolled`
60
- * exists.
81
+ * and `collapsed` exist. Neither watches the page: only the app knows which
82
+ * element scrolls, so both are set from its own scroll handler.
61
83
  */
62
- function AppBar({ align = "start", headline, leading, render, scrolled = false, size = "small", subtitle, trailing, ...props$1 }) {
63
- const height = subtitle === void 0 || subtitle === null ? HEIGHTS[size].plain : HEIGHTS[size].withSubtitle;
84
+ function AppBar({ align = "start", collapsed = false, contentInset = DEFAULT_CONTENT_INSET, contentMaxInlineSize = "none", headline, leading, render, scrolled = false, size = "small", subtitle, trailing, ...props$1 }) {
85
+ const collapse = collapsed && size !== "small";
86
+ const expandedHeight = subtitle === void 0 || subtitle === null ? HEIGHTS[size].plain : HEIGHTS[size].withSubtitle;
87
+ const height = collapse ? COLLAPSED_HEIGHT : expandedHeight;
64
88
  return useRender({
65
89
  defaultTagName: "header",
66
90
  props: {
67
91
  ...props$1,
68
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
69
- leading === void 0 ? null : /* @__PURE__ */ jsx("div", {
70
- className: "x6s0dn4 x78zum5 x2lah0s xzoy561",
71
- children: leading
72
- }),
73
- /* @__PURE__ */ jsxs("div", {
74
- ...{
75
- 0: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll x18fpw6u" },
76
- 1: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll x18fpw6u x2b8uid" }
77
- }[!!(align === "center") << 0],
78
- children: [headline === void 0 ? null : /* @__PURE__ */ jsx(Text, {
79
- render: HEADING_1,
80
- variant: HEADLINE_VARIANT[size],
81
- children: headline
82
- }), subtitle === void 0 ? null : /* @__PURE__ */ jsx(Text, {
83
- render: PARAGRAPH,
84
- tone: "muted",
85
- variant: "bodyMedium",
86
- children: subtitle
87
- })]
88
- }),
89
- trailing === void 0 ? null : /* @__PURE__ */ jsx("div", {
90
- className: "x6s0dn4 x78zum5 x2lah0s xzoy561",
91
- children: trailing
92
- })
93
- ] }),
92
+ children: /* @__PURE__ */ jsxs("div", {
93
+ ...props(styles.row, rowStyles.root(contentInset, contentMaxInlineSize)),
94
+ children: [
95
+ leading === void 0 ? null : /* @__PURE__ */ jsx("div", {
96
+ className: "x6s0dn4 x78zum5 x2lah0s xzoy561",
97
+ children: leading
98
+ }),
99
+ /* @__PURE__ */ jsxs("div", {
100
+ ...{
101
+ 0: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll xaope02" },
102
+ 1: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll xaope02 x2b8uid" }
103
+ }[!!(align === "center") << 0],
104
+ children: [headline === void 0 ? null : /* @__PURE__ */ jsx(Text, {
105
+ className: "xioksi3 x1nxaspg x1seskgd",
106
+ render: HEADING_1,
107
+ variant: collapse ? HEADLINE_VARIANT.small : HEADLINE_VARIANT[size],
108
+ children: headline
109
+ }), subtitle === void 0 || collapse ? null : /* @__PURE__ */ jsx(Text, {
110
+ render: PARAGRAPH,
111
+ tone: "muted",
112
+ variant: "bodyMedium",
113
+ children: subtitle
114
+ })]
115
+ }),
116
+ trailing === void 0 ? null : /* @__PURE__ */ jsx("div", {
117
+ className: "x6s0dn4 x78zum5 x2lah0s xzoy561",
118
+ children: trailing
119
+ })
120
+ ]
121
+ }),
94
122
  ...mergeStyles(props(styles.root, heightStyles.root(height), scrolled && styles.scrolled), props$1)
95
123
  },
96
124
  render
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/app-bar/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useRender } from '@base-ui/react/use-render';\nimport * as stylex from '@stylexjs/stylex';\nimport { mergeStyles } from '../../styles/merge';\nimport '../../tokens/design.tokens.stylex';\nimport { colors, spacing } from '../../tokens/design.tokens.stylex';\nimport Text from '../text';\n\n// Material 3's app bar, in the three sizes M3 Expressive recommends.\n//\n// The spec offers four variants. `search` is absent because it opens M3's\n// search view when selected, and that is a component in its own right that\n// this library does not have — half of a search bar would be worse than none.\n// The other three are here.\n//\n// `medium` and `large` are M3 Expressive's *flexible* bars. The baseline\n// medium and large ones they replaced are marked \"not recommended\" in the\n// spec, so there is no ambiguity to protect against and the shorter names are\n// the ones worth having at a call site.\n//\n// M3 also merged the old center-aligned variant into small as a configuration\n// rather than a variant, which is why alignment is a prop here and applies to\n// every size.\n//\n// Heights are M3's, and they are minimums rather than fixed. The spec says the\n// flexible bars \"hug the text contents\", and Expressive added multi-line\n// support and text wrapping, so a headline long enough to wrap has to be able\n// to make its bar taller. Fixing the height would truncate exactly the case\n// the variant was redesigned for.\nconst HEIGHTS = {\n large: {\n plain: '120px',\n withSubtitle: '152px'\n },\n medium: {\n plain: '112px',\n withSubtitle: '136px'\n },\n small: {\n plain: '64px',\n withSubtitle: '64px'\n }\n} as const;\nconst styles = {\n root: {\n kGNEyG: \"x6s0dn4\",\n kWkggS: \"x14s9m32\",\n kB7OPa: \"x9f619\",\n k1xSpc: \"x78zum5\",\n kOIVth: \"xzoy561\",\n kg3NbH: \"ximhkdq\",\n kIyJzY: \"xx6bhzk\",\n k1ekBW: \"x15406qy\",\n $$css: true\n },\n scrolled: {\n kWkggS: \"x1mjdul6\",\n $$css: true\n }\n};\n\n// One entry per size, and each names a type role rather than a figure. M3's\n// own tokens are `md.comp.app-bar.<size>.title.font`, aliases onto the type\n// scale rather than sizes of their own, so this maps to the scale the same\n// way and lets Text apply it.\nconst HEADLINE_VARIANT = {\n large: 'headlineMedium',\n medium: 'headlineSmall',\n small: 'titleLarge'\n} as const;\n\n// The headline is the page's title in M3's model, so it is the page's <h1>.\n// A bar nested somewhere that already has one can pass its own heading as\n// `headline` instead.\n// oxlint-disable-next-line jsx-a11y/heading-has-content -- filled by useRender\nconst HEADING_1 = <h1 />;\nconst PARAGRAPH = <p />;\n\n// Height is the one value the call site does not choose, and it depends on two\n// props at once, so it is a function style rather than one key per\n// combination. StyleX writes it to a custom property inline, the same\n// mechanism Feed uses for its cell minimum.\nconst _temp = {\n kAzted: \"x1chvrdh\",\n \"$$css\": true\n};\nconst heightStyles = {\n root: (minBlockSize: string) => [_temp, {\n \"--x-minBlockSize\": (val => typeof val === \"number\" ? val + \"px\" : val != null ? val : undefined)(minBlockSize)\n }]\n};\ntype AppBarProps = Omit<useRender.ComponentProps<'header'>, 'children'> & {\n /**\n * Which of M3's alignments the text takes. `center` is the configuration\n * that replaced the old center-aligned variant.\n * @default 'start'\n */\n align?: 'center' | 'start';\n /** The page's title. Wraps rather than truncating, and grows the bar with it. */\n headline?: ReactNode;\n /** Usually a back or menu icon button. Sits before the text. */\n leading?: ReactNode;\n /**\n * Whether the content beneath has been scrolled. M3 separates a bar from\n * scrolled content with a fill colour rather than a shadow, and this is what\n * applies it.\n *\n * Controlled, with no listener of its own: only the app knows which element\n * scrolls, and a bar that watched the window would be wrong inside every\n * pane and dialog that scrolls independently.\n * @default false\n */\n scrolled?: boolean;\n /**\n * Height and headline size. `medium` and `large` are M3 Expressive's\n * flexible bars, which hug their text — the baseline variants they replaced\n * are no longer recommended and are not offered.\n * @default 'small'\n */\n size?: 'large' | 'medium' | 'small';\n /** A second line under the headline. Makes `medium` and `large` taller. */\n subtitle?: ReactNode;\n /** Actions, at the far end. Usually icon buttons. */\n trailing?: ReactNode;\n};\n\n/**\n * Material 3's app bar: the container at the top of a page carrying its title,\n * one or two actions, and the way back out.\n *\n * Three sizes. `small` is a fixed 64px bar for a page whose title is a label;\n * `medium` and `large` are the flexible bars, which give the headline a larger\n * type role and grow to fit a subtitle or a headline that wraps.\n *\n * It paints its own surface, unlike the layout components, because separating\n * itself from the content beneath is the job — which is also why `scrolled`\n * exists.\n */\nfunction AppBar({\n align = 'start',\n headline,\n leading,\n render,\n scrolled = false,\n size = 'small',\n subtitle,\n trailing,\n ...props\n}: AppBarProps) {\n const height = subtitle === undefined || subtitle === null ? HEIGHTS[size].plain : HEIGHTS[size].withSubtitle;\n return useRender({\n defaultTagName: 'header',\n props: {\n ...props,\n children: <>\n {leading === undefined ? null : <div className=\"x6s0dn4 x78zum5 x2lah0s xzoy561\">{leading}</div>}\n <div {...{\n 0: {\n className: \"x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll x18fpw6u\"\n },\n 1: {\n className: \"x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll x18fpw6u x2b8uid\"\n }\n }[!!(align === 'center') << 0]}>\n {headline === undefined ? null : <Text render={HEADING_1} variant={HEADLINE_VARIANT[size]}>\n {headline}\n </Text>}\n {subtitle === undefined ? null : <Text render={PARAGRAPH} tone=\"muted\" variant=\"bodyMedium\">\n {subtitle}\n </Text>}\n </div>\n {trailing === undefined ? null : <div className=\"x6s0dn4 x78zum5 x2lah0s xzoy561\">{trailing}</div>}\n </>,\n ...mergeStyles(stylex.props(styles.root, heightStyles.root(height), scrolled && styles.scrolled), props)\n },\n render\n });\n}\nexport type { AppBarProps };\nexport default AppBar;"],"mappings":";;;;;;AA6BA,MAAM,UAAU;CACd,OAAO;EACL,OAAO;EACP,cAAc;CAChB;CACA,QAAQ;EACN,OAAO;EACP,cAAc;CAChB;CACA,OAAO;EACL,OAAO;EACP,cAAc;CAChB;AACF;AACA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;AACF;AAMA,MAAM,mBAAmB;CACvB,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAMA,MAAM,YAAY,oBAAC,MAAD,CAAG,CAAA;AACrB,MAAM,YAAY,oBAAC,KAAD,CAAE,CAAA;AAMpB,MAAM,QAAQ;CACZ,QAAQ;CACR,SAAS;AACX;AACA,MAAM,eAAe,EACnB,OAAO,iBAAyB,CAAC,OAAO,EACtC,sBAAqB,QAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,OAAO,MAAM,KAAA,EAAA,CAAW,YAAY,EAChH,CAAC,EACH;;;;;;;;;;;;;AAgDA,SAAS,OAAO,EACd,QAAQ,SACR,UACA,SACA,QACA,WAAW,OACX,OAAO,SACP,UACA,UACA,GAAG,WACW;CACd,MAAM,SAAS,aAAa,KAAA,KAAa,aAAa,OAAO,QAAQ,KAAK,CAAC,QAAQ,QAAQ,KAAK,CAAC;CACjG,OAAO,UAAU;EACf,gBAAgB;EAChB,OAAO;GACL,GAAG;GACH,UAAU,qBAAA,UAAA,EAAA,UAAA;IACL,YAAY,KAAA,IAAY,OAAO,oBAAC,OAAD;KAAK,WAAU;KAAmC,UAAA;IAAa,CAAA;IAC/F,qBAAC,OAAD;KAAK,GAAI;MACT,GAAG,EACD,WAAW,oEACb;MACA,GAAG,EACD,WAAW,4EACb;KACF,EAAE,CAAC,EAAE,UAAU,aAAa;KAP1B,UAAA,CAQG,aAAa,KAAA,IAAY,OAAO,oBAAC,MAAD;MAAM,QAAQ;MAAW,SAAS,iBAAiB;MAC/E,UAAA;KACG,CAAA,GACP,aAAa,KAAA,IAAY,OAAO,oBAAC,MAAD;MAAM,QAAQ;MAAW,MAAK;MAAQ,SAAQ;MAC1E,UAAA;KACG,CAAA,CACL;;IACJ,aAAa,KAAA,IAAY,OAAO,oBAAC,OAAD;KAAK,WAAU;KAAmC,UAAA;IAAc,CAAA;GACnG,EAAA,CAAA;GACF,GAAG,YAAY,MAAa,OAAO,MAAM,aAAa,KAAK,MAAM,GAAG,YAAY,OAAO,QAAQ,GAAG,OAAK;EACzG;EACA;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/app-bar/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useRender } from '@base-ui/react/use-render';\nimport * as stylex from '@stylexjs/stylex';\nimport { mergeStyles } from '../../styles/merge';\nimport '../../tokens/design.tokens.stylex';\nimport { colors, motion, spacing } from '../../tokens/design.tokens.stylex';\nimport Text from '../text';\n\n// Material 3's app bar, in the three sizes M3 Expressive recommends.\n//\n// The spec offers four variants. `search` is absent because it opens M3's\n// search view when selected, and that is a component in its own right that\n// this library does not have — half of a search bar would be worse than none.\n// The other three are here.\n//\n// `medium` and `large` are M3 Expressive's *flexible* bars. The baseline\n// medium and large ones they replaced are marked \"not recommended\" in the\n// spec, so there is no ambiguity to protect against and the shorter names are\n// the ones worth having at a call site.\n//\n// M3 also merged the old center-aligned variant into small as a configuration\n// rather than a variant, which is why alignment is a prop here and applies to\n// every size.\n//\n// Heights are M3's, and they are minimums rather than fixed. The spec says the\n// flexible bars \"hug the text contents\", and Expressive added multi-line\n// support and text wrapping, so a headline long enough to wrap has to be able\n// to make its bar taller. Fixing the height would truncate exactly the case\n// the variant was redesigned for.\n\n// How far the headline sits inside whatever comes before it, and the figure\n// an icon button's glyph is inset from the edge of its own touch target. M3\n// leans on the two agreeing, which is what makes a bar with a leading icon\n// and one without start their text in the same place.\nconst HEADLINE_OFFSET = '12px';\n\n// Where the bar's visible content starts by default, which is M3's own\n// margin for a top app bar and the same margin its body content takes.\nconst DEFAULT_CONTENT_INSET = '16px';\nconst HEIGHTS = {\n large: {\n plain: '120px',\n withSubtitle: '152px'\n },\n medium: {\n plain: '112px',\n withSubtitle: '136px'\n },\n small: {\n plain: '64px',\n withSubtitle: '64px'\n }\n} as const;\n\n// What a flexible bar collapses to, which is the small bar exactly. M3 does\n// not describe the collapsed large bar as a state of its own — it describes it\n// as becoming the small bar, which is why this reads off the same table rather\n// than being a fourth height.\nconst COLLAPSED_HEIGHT = HEIGHTS.small.plain;\nconst styles = {\n root: {\n kGNEyG: \"x6s0dn4\",\n kWkggS: \"x14s9m32\",\n kB7OPa: \"x9f619\",\n k1xSpc: \"x78zum5\",\n kjj79g: \"xl56j7k\",\n kIyJzY: \"xioksi3\",\n k1ekBW: \"x8u3r54\",\n kAMwcw: \"x1seskgd\",\n $$css: true\n },\n row: {\n kGNEyG: \"x6s0dn4\",\n kB7OPa: \"x9f619\",\n k1xSpc: \"x78zum5\",\n kOIVth: \"xzoy561\",\n kzqmXN: \"xh8yej3\",\n kUOVxO: \"xvueqy4\",\n $$css: true\n },\n scrolled: {\n kWkggS: \"x1mjdul6\",\n $$css: true\n }\n};\n\n// One entry per size, and each names a type role rather than a figure. M3's\n// own tokens are `md.comp.app-bar.<size>.title.font`, aliases onto the type\n// scale rather than sizes of their own, so this maps to the scale the same\n// way and lets Text apply it.\nconst HEADLINE_VARIANT = {\n large: 'headlineMedium',\n medium: 'headlineSmall',\n small: 'titleLarge'\n} as const;\n\n// The headline is the page's title in M3's model, so it is the page's <h1>.\n// A bar nested somewhere that already has one can pass its own heading as\n// `headline` instead.\n// oxlint-disable-next-line jsx-a11y/heading-has-content -- filled by useRender\nconst HEADING_1 = <h1 />;\nconst PARAGRAPH = <p />;\n\n// Height is the one value the call site does not choose, and it depends on two\n// props at once, so it is a function style rather than one key per\n// combination. StyleX writes it to a custom property inline, the same\n// mechanism Feed uses for its cell minimum.\nconst _temp = {\n kAzted: \"x1chvrdh\",\n \"$$css\": true\n};\nconst heightStyles = {\n root: (minBlockSize: string) => [_temp, {\n \"--x-minBlockSize\": (val => typeof val === \"number\" ? val + \"px\" : val != null ? val : undefined)(minBlockSize)\n }]\n};\n\n// The row's measure and inset both come from the call site, so both are\n// function styles too. The inset is where the bar's visible content starts,\n// and the row carries `contentInset` minus the offset the text block already\n// holds — which is what keeps a leading icon's glyph and a headline with no\n// icon before it starting in the same place.\nconst _temp2 = {\n ks0D6T: \"x13i5qev\",\n kg3NbH: \"x30g6up\",\n \"$$css\": true\n};\nconst rowStyles = {\n root: (contentInset: string, maxInlineSize: string) => [_temp2, {\n \"--x-maxInlineSize\": (val => typeof val === \"number\" ? val + \"px\" : val != null ? val : undefined)(maxInlineSize),\n \"--x-paddingInline\": (val => typeof val === \"number\" ? val + \"px\" : val != null ? val : undefined)(`calc(${contentInset} - ${HEADLINE_OFFSET})`)\n }]\n};\ntype AppBarProps = Omit<useRender.ComponentProps<'header'>, 'children'> & {\n /**\n * Which of M3's alignments the text takes. `center` is the configuration\n * that replaced the old center-aligned variant.\n * @default 'start'\n */\n align?: 'center' | 'start';\n /**\n * Collapses a flexible bar to the height and headline of the small one, for\n * a pinned bar over content that has been scrolled. M3 specifies it for the\n * flexible sizes, and without it a pinned `large` costs 152px of the\n * viewport for as long as the page is open.\n *\n * Controlled, and for the same reason `scrolled` is: only the app knows\n * which element scrolls, and how far into it the bar should have finished\n * collapsing. Usually set from the same handler.\n *\n * Ignored on `small`, which is already the height this collapses to. The\n * subtitle goes with the height, since there is no room for a second line\n * in the small bar.\n *\n * Collapsing hands the page back the height the bar gives up, which\n * shortens the scroll range. Where the content is barely longer than the\n * viewport that can pull the position back under whatever threshold set\n * this, expanding the bar again — so pick a threshold with room under it,\n * or drive this from something other than a raw offset.\n * @default false\n */\n collapsed?: boolean;\n /**\n * Where the bar's content starts, measured to the headline. A leading slot\n * sits 12px before that, which is the room an icon button's own padding\n * fills around its glyph — M3's arrangement, and what makes a bar with an\n * icon and one without start their text in the same place.\n *\n * The default is M3's own margin for a top app bar, which is also the\n * margin it gives body content, so a bar and the page beneath line up\n * without either being told about the other. Set it to the page's gutter\n * where that differs.\n * @default '16px'\n */\n contentInset?: string;\n /**\n * How wide the bar's contents may run before they stop growing, with the\n * row centred in whatever is left. The bar's own surface still paints edge\n * to edge.\n *\n * This is what lines a full-bleed bar up with a page whose content sits in\n * a measure: give it the page's measure, and `contentInset` the page's\n * gutter. Unset, the row is as wide as the bar.\n */\n contentMaxInlineSize?: string;\n /** The page's title. Wraps rather than truncating, and grows the bar with it. */\n headline?: ReactNode;\n /** Usually a back or menu icon button. Sits before the text. */\n leading?: ReactNode;\n /**\n * Whether the content beneath has been scrolled. M3 separates a bar from\n * scrolled content with a fill colour rather than a shadow, and this is what\n * applies it.\n *\n * Controlled, with no listener of its own: only the app knows which element\n * scrolls, and a bar that watched the window would be wrong inside every\n * pane and dialog that scrolls independently.\n * @default false\n */\n scrolled?: boolean;\n /**\n * Height and headline size. `medium` and `large` are M3 Expressive's\n * flexible bars, which hug their text — the baseline variants they replaced\n * are no longer recommended and are not offered.\n * @default 'small'\n */\n size?: 'large' | 'medium' | 'small';\n /** A second line under the headline. Makes `medium` and `large` taller. */\n subtitle?: ReactNode;\n /** Actions, at the far end. Usually icon buttons. */\n trailing?: ReactNode;\n};\n\n/**\n * Material 3's app bar: the container at the top of a page carrying its title,\n * one or two actions, and the way back out.\n *\n * Three sizes. `small` is a fixed 64px bar for a page whose title is a label;\n * `medium` and `large` are the flexible bars, which give the headline a larger\n * type role and grow to fit a subtitle or a headline that wraps.\n *\n * It paints its own surface, unlike the layout components, because separating\n * itself from the content beneath is the job — which is also why `scrolled`\n * and `collapsed` exist. Neither watches the page: only the app knows which\n * element scrolls, so both are set from its own scroll handler.\n */\nfunction AppBar({\n align = 'start',\n collapsed = false,\n contentInset = DEFAULT_CONTENT_INSET,\n contentMaxInlineSize = 'none',\n headline,\n leading,\n render,\n scrolled = false,\n size = 'small',\n subtitle,\n trailing,\n ...props\n}: AppBarProps) {\n // `small` is already what the flexible bars collapse to, so collapsing it is\n // a no-op rather than an error. A bar whose size is chosen at the call site\n // from a breakpoint would otherwise have to guard the prop as well.\n const collapse = collapsed && size !== 'small';\n const expandedHeight = subtitle === undefined || subtitle === null ? HEIGHTS[size].plain : HEIGHTS[size].withSubtitle;\n const height = collapse ? COLLAPSED_HEIGHT : expandedHeight;\n return useRender({\n defaultTagName: 'header',\n props: {\n ...props,\n children: <div {...stylex.props(styles.row, rowStyles.root(contentInset, contentMaxInlineSize))}>\n {leading === undefined ? null : <div className=\"x6s0dn4 x78zum5 x2lah0s xzoy561\">{leading}</div>}\n <div {...{\n 0: {\n className: \"x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll xaope02\"\n },\n 1: {\n className: \"x9f619 x78zum5 xdt5ytf x1iyjqo2 xl56j7k xeuugli x1cmllll xaope02 x2b8uid\"\n }\n }[!!(align === 'center') << 0]}>\n {headline === undefined ? null : <Text className=\"xioksi3 x1nxaspg x1seskgd\" render={HEADING_1} variant={collapse ? HEADLINE_VARIANT.small : HEADLINE_VARIANT[size]}>\n {headline}\n </Text>}\n {subtitle === undefined || collapse ? null : <Text render={PARAGRAPH} tone=\"muted\" variant=\"bodyMedium\">\n {subtitle}\n </Text>}\n </div>\n {trailing === undefined ? null : <div className=\"x6s0dn4 x78zum5 x2lah0s xzoy561\">{trailing}</div>}\n </div>,\n ...mergeStyles(stylex.props(styles.root, heightStyles.root(height), scrolled && styles.scrolled), props)\n },\n render\n });\n}\nexport type { AppBarProps };\nexport default AppBar;"],"mappings":";;;;;;AAkCA,MAAM,kBAAkB;AAIxB,MAAM,wBAAwB;AAC9B,MAAM,UAAU;CACd,OAAO;EACL,OAAO;EACP,cAAc;CAChB;CACA,QAAQ;EACN,OAAO;EACP,cAAc;CAChB;CACA,OAAO;EACL,OAAO;EACP,cAAc;CAChB;AACF;AAMA,MAAM,mBAAmB,QAAQ,MAAM;AACvC,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,KAAK;EACH,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;AACF;AAMA,MAAM,mBAAmB;CACvB,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAMA,MAAM,YAAY,oBAAC,MAAD,CAAG,CAAA;AACrB,MAAM,YAAY,oBAAC,KAAD,CAAE,CAAA;AAMpB,MAAM,QAAQ;CACZ,QAAQ;CACR,SAAS;AACX;AACA,MAAM,eAAe,EACnB,OAAO,iBAAyB,CAAC,OAAO,EACtC,sBAAqB,QAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,OAAO,MAAM,KAAA,EAAA,CAAW,YAAY,EAChH,CAAC,EACH;AAOA,MAAM,SAAS;CACb,QAAQ;CACR,QAAQ;CACR,SAAS;AACX;AACA,MAAM,YAAY,EAChB,OAAO,cAAsB,kBAA0B,CAAC,QAAQ;CAC9D,uBAAsB,QAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,OAAO,MAAM,KAAA,EAAA,CAAW,aAAa;CAChH,uBAAsB,QAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,OAAO,MAAM,KAAA,EAAA,CAAW,QAAQ,aAAY,KAAM,gBAAe,EAAG;AACjJ,CAAC,EACH;;;;;;;;;;;;;;AA8FA,SAAS,OAAO,EACd,QAAQ,SACR,YAAY,OACZ,eAAe,uBACf,uBAAuB,QACvB,UACA,SACA,QACA,WAAW,OACX,OAAO,SACP,UACA,UACA,GAAG,WACW;CAId,MAAM,WAAW,aAAa,SAAS;CACvC,MAAM,iBAAiB,aAAa,KAAA,KAAa,aAAa,OAAO,QAAQ,KAAK,CAAC,QAAQ,QAAQ,KAAK,CAAC;CACzG,MAAM,SAAS,WAAW,mBAAmB;CAC7C,OAAO,UAAU;EACf,gBAAgB;EAChB,OAAO;GACL,GAAG;GACH,UAAU,qBAAC,OAAD;IAAK,GAAI,MAAa,OAAO,KAAK,UAAU,KAAK,cAAc,oBAAoB,CAAC;IAApF,UAAA;KACL,YAAY,KAAA,IAAY,OAAO,oBAAC,OAAD;MAAK,WAAU;MAAmC,UAAA;KAAa,CAAA;KAC/F,qBAAC,OAAD;MAAK,GAAI;OACT,GAAG,EACD,WAAW,mEACb;OACA,GAAG,EACD,WAAW,2EACb;MACF,EAAE,CAAC,EAAE,UAAU,aAAa;MAP1B,UAAA,CAQG,aAAa,KAAA,IAAY,OAAO,oBAAC,MAAD;OAAM,WAAU;OAA4B,QAAQ;OAAW,SAAS,WAAW,iBAAiB,QAAQ,iBAAiB;OACzJ,UAAA;MACG,CAAA,GACP,aAAa,KAAA,KAAa,WAAW,OAAO,oBAAC,MAAD;OAAM,QAAQ;OAAW,MAAK;OAAQ,SAAQ;OACtF,UAAA;MACG,CAAA,CACL;;KACJ,aAAa,KAAA,IAAY,OAAO,oBAAC,OAAD;MAAK,WAAU;MAAmC,UAAA;KAAc,CAAA;IAC9F;;GACP,GAAG,YAAY,MAAa,OAAO,MAAM,aAAa,KAAK,MAAM,GAAG,YAAY,OAAO,QAAQ,GAAG,OAAK;EACzG;EACA;CACF,CAAC;AACH"}
@@ -1,6 +1,19 @@
1
1
  import { useRender } from "@base-ui/react/use-render";
2
2
  //#region src/components/text/index.d.ts
3
3
  type TextProps = useRender.ComponentProps<'span'> & {
4
+ /**
5
+ * Renders a `<p>` rather than the default `<span>`, for a block of prose
6
+ * rather than a run of text inside one.
7
+ *
8
+ * The paragraph carries no margin of its own, the same as every other Text.
9
+ * The space between two paragraphs belongs to whatever holds both of them,
10
+ * which is where it can be a step of the spacing scale rather than a
11
+ * browser default.
12
+ *
13
+ * Ignored when `render` is given, since that names the element outright.
14
+ * @default false
15
+ */
16
+ block?: boolean;
4
17
  /**
5
18
  * The color role to render in. `inherit` takes the color of the nearest
6
19
  * colored ancestor instead of setting one.
@@ -13,7 +26,7 @@ type TextProps = useRender.ComponentProps<'span'> & {
13
26
  */
14
27
  variant?: 'bodyLarge' | 'bodyMedium' | 'bodySmall' | 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall';
15
28
  };
16
- declare function Text({ render, tone, variant, ...props }: TextProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
29
+ declare function Text({ block, render, tone, variant, ...props }: TextProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
17
30
  //#endregion
18
31
  export { type TextProps, Text as default };
19
32
  //# sourceMappingURL=index.d.ts.map
@@ -159,9 +159,9 @@ const tones = {
159
159
  $$css: true
160
160
  }
161
161
  };
162
- function Text({ render, tone = "default", variant = "bodyMedium", ...props$1 }) {
162
+ function Text({ block = false, render, tone = "default", variant = "bodyMedium", ...props$1 }) {
163
163
  return useRender({
164
- defaultTagName: "span",
164
+ defaultTagName: block ? "p" : "span",
165
165
  props: {
166
166
  ...props$1,
167
167
  ...mergeStyles(props(styles.base, styles[variant], tones[tone]), props$1)
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/text/index.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render';\nimport * as stylex from '@stylexjs/stylex';\nimport { mergeStyles } from '../../styles/merge';\nimport '../../tokens/design.tokens.stylex';\nimport { colors, typography } from '../../tokens/design.tokens.stylex';\n\n// One key per style in the 15-entry type scale, named exactly as the token\n// group names it, so `styles[variant]` indexes straight off the prop with no\n// lookup table to keep in sync. Every style sets all five fields the scale\n// defines rather than inheriting any of them: a Text nested inside another\n// Text would otherwise pick up the outer line-height or tracking, which is\n// the kind of drift the scale exists to prevent.\n//\n// `margin: 0` is the one UA reset here. Text renders a <span> by default, but\n// `render` is how a consumer reaches for semantic markup — `<Text\n// render={<h2 />}>` — and an h2's UA margin would then vary the spacing\n// around otherwise identical type.\nconst styles = {\n base: {\n kB7OPa: \"x9f619\",\n kogj98: \"x1ghz6dp\",\n $$css: true\n },\n bodyLarge: {\n kMv6JI: \"x16svsjw\",\n kGuDYH: \"x1848etk\",\n k63SB2: \"x1c52epr\",\n kb6lSQ: \"x1qn8t5l\",\n kLWn49: \"xh7dzej\",\n $$css: true\n },\n bodyMedium: {\n kMv6JI: \"xpo2rfv\",\n kGuDYH: \"x193rfdd\",\n k63SB2: \"x1hews6w\",\n kb6lSQ: \"xn43zvd\",\n kLWn49: \"xv61bfh\",\n $$css: true\n },\n bodySmall: {\n kMv6JI: \"x1yicsit\",\n kGuDYH: \"x1b35g6e\",\n k63SB2: \"x9d6959\",\n kb6lSQ: \"xwth0xh\",\n kLWn49: \"xybisxh\",\n $$css: true\n },\n displayLarge: {\n kMv6JI: \"xjaampf\",\n kGuDYH: \"xuyiek4\",\n k63SB2: \"x1n2oj23\",\n kb6lSQ: \"x1hxa6v5\",\n kLWn49: \"x1iwmiir\",\n $$css: true\n },\n displayMedium: {\n kMv6JI: \"x1gi70vc\",\n kGuDYH: \"xlzzeuu\",\n k63SB2: \"xh9quta\",\n kb6lSQ: \"x148mp24\",\n kLWn49: \"xs5hucj\",\n $$css: true\n },\n displaySmall: {\n kMv6JI: \"x1eslvzl\",\n kGuDYH: \"x1g52wnc\",\n k63SB2: \"x1x664hp\",\n kb6lSQ: \"x1rnjaw4\",\n kLWn49: \"x92ldg4\",\n $$css: true\n },\n headlineLarge: {\n kMv6JI: \"x1vsmgmf\",\n kGuDYH: \"xh9c4x\",\n k63SB2: \"x1y15vu6\",\n kb6lSQ: \"xoqu3bc\",\n kLWn49: \"x10x9d6x\",\n $$css: true\n },\n headlineMedium: {\n kMv6JI: \"x18s19nt\",\n kGuDYH: \"xcohea8\",\n k63SB2: \"x1mtpwof\",\n kb6lSQ: \"x1kuyk5j\",\n kLWn49: \"xucx483\",\n $$css: true\n },\n headlineSmall: {\n kMv6JI: \"x1fz0g89\",\n kGuDYH: \"xkb6epg\",\n k63SB2: \"x76yrz5\",\n kb6lSQ: \"x1s1qhaa\",\n kLWn49: \"x1voeury\",\n $$css: true\n },\n labelLarge: {\n kMv6JI: \"xkknnkr\",\n kGuDYH: \"x14zspaw\",\n k63SB2: \"x1v9cpvo\",\n kb6lSQ: \"x155f00i\",\n kLWn49: \"x1lz68p4\",\n $$css: true\n },\n labelMedium: {\n kMv6JI: \"xp6n8n2\",\n kGuDYH: \"xkucv67\",\n k63SB2: \"x14hhc5u\",\n kb6lSQ: \"x12utw95\",\n kLWn49: \"xvzb9hk\",\n $$css: true\n },\n labelSmall: {\n kMv6JI: \"xs1w0rn\",\n kGuDYH: \"x1n35her\",\n k63SB2: \"x1su64ma\",\n kb6lSQ: \"xdlnduh\",\n kLWn49: \"xaomhve\",\n $$css: true\n },\n titleLarge: {\n kMv6JI: \"xdy5w5q\",\n kGuDYH: \"xete2z3\",\n k63SB2: \"x14ijtet\",\n kb6lSQ: \"xug9e1u\",\n kLWn49: \"x2divse\",\n $$css: true\n },\n titleMedium: {\n kMv6JI: \"x46eyla\",\n kGuDYH: \"x5ehqhr\",\n k63SB2: \"xrf6cl6\",\n kb6lSQ: \"x1g135zh\",\n kLWn49: \"x1ggk5va\",\n $$css: true\n },\n titleSmall: {\n kMv6JI: \"xrgwony\",\n kGuDYH: \"x12s497w\",\n k63SB2: \"x1szcwkt\",\n kb6lSQ: \"xo803ec\",\n kLWn49: \"xyl1r0q\",\n $$css: true\n }\n};\n\n// Tone is a separate axis from variant so the two compose freely — any of the\n// 15 scale styles in any of these colors. Kept to the roles text actually\n// takes on a surface; a consumer needing something outside this set uses\n// `inherit` and colors the parent, rather than this growing a case per role.\nconst tones = {\n default: {\n kMwMTN: \"x139gbkf\",\n $$css: true\n },\n error: {\n kMwMTN: \"x4ii1qj\",\n $$css: true\n },\n inherit: {\n kMwMTN: \"x1heor9g\",\n $$css: true\n },\n muted: {\n kMwMTN: \"x1w5n3ug\",\n $$css: true\n },\n negative: {\n kMwMTN: \"x12gp0t\",\n $$css: true\n },\n positive: {\n kMwMTN: \"x4no3js\",\n $$css: true\n },\n primary: {\n kMwMTN: \"x1mxwv8y\",\n $$css: true\n }\n};\ntype TextProps = useRender.ComponentProps<'span'> & {\n /**\n * The color role to render in. `inherit` takes the color of the nearest\n * colored ancestor instead of setting one.\n * @default 'default'\n */\n tone?: 'default' | 'error' | 'inherit' | 'muted' | 'negative' | 'positive' | 'primary';\n /**\n * Which style of the type scale to render.\n * @default 'bodyMedium'\n */\n variant?: 'bodyLarge' | 'bodyMedium' | 'bodySmall' | 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall';\n};\nfunction Text({\n render,\n tone = 'default',\n variant = 'bodyMedium',\n ...props\n}: TextProps) {\n return useRender({\n defaultTagName: 'span',\n props: {\n ...props,\n ...mergeStyles(stylex.props(styles.base, styles[variant], tones[tone]), props)\n },\n render\n });\n}\nexport type { TextProps };\nexport default Text;"],"mappings":";;;;AAiBA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,gBAAgB;EACd,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,aAAa;EACX,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,aAAa;EACX,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;AACF;AAMA,MAAM,QAAQ;CACZ,SAAS;EACP,QAAQ;EACR,OAAO;CACT;CACA,OAAO;EACL,QAAQ;EACR,OAAO;CACT;CACA,SAAS;EACP,QAAQ;EACR,OAAO;CACT;CACA,OAAO;EACL,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;CACA,SAAS;EACP,QAAQ;EACR,OAAO;CACT;AACF;AAcA,SAAS,KAAK,EACZ,QACA,OAAO,WACP,UAAU,cACV,GAAG,WACS;CACZ,OAAO,UAAU;EACf,gBAAgB;EAChB,OAAO;GACL,GAAG;GACH,GAAG,YAAY,MAAa,OAAO,MAAM,OAAO,UAAU,MAAM,KAAK,GAAG,OAAK;EAC/E;EACA;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/text/index.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render';\nimport * as stylex from '@stylexjs/stylex';\nimport { mergeStyles } from '../../styles/merge';\nimport '../../tokens/design.tokens.stylex';\nimport { colors, typography } from '../../tokens/design.tokens.stylex';\n\n// One key per style in the 15-entry type scale, named exactly as the token\n// group names it, so `styles[variant]` indexes straight off the prop with no\n// lookup table to keep in sync. Every style sets all five fields the scale\n// defines rather than inheriting any of them: a Text nested inside another\n// Text would otherwise pick up the outer line-height or tracking, which is\n// the kind of drift the scale exists to prevent.\n//\n// `margin: 0` is the one UA reset here. Text renders a <span> by default, but\n// `block` renders a <p> and `render` is how a consumer reaches for any other\n// semantic markup — `<Text render={<h2 />}>` — and the UA margin on either\n// would then vary the spacing around otherwise identical type. The space\n// between two blocks of text is the job of whatever holds them, which is\n// where it can come from the spacing scale.\nconst styles = {\n base: {\n kB7OPa: \"x9f619\",\n kogj98: \"x1ghz6dp\",\n $$css: true\n },\n bodyLarge: {\n kMv6JI: \"x16svsjw\",\n kGuDYH: \"x1848etk\",\n k63SB2: \"x1c52epr\",\n kb6lSQ: \"x1qn8t5l\",\n kLWn49: \"xh7dzej\",\n $$css: true\n },\n bodyMedium: {\n kMv6JI: \"xpo2rfv\",\n kGuDYH: \"x193rfdd\",\n k63SB2: \"x1hews6w\",\n kb6lSQ: \"xn43zvd\",\n kLWn49: \"xv61bfh\",\n $$css: true\n },\n bodySmall: {\n kMv6JI: \"x1yicsit\",\n kGuDYH: \"x1b35g6e\",\n k63SB2: \"x9d6959\",\n kb6lSQ: \"xwth0xh\",\n kLWn49: \"xybisxh\",\n $$css: true\n },\n displayLarge: {\n kMv6JI: \"xjaampf\",\n kGuDYH: \"xuyiek4\",\n k63SB2: \"x1n2oj23\",\n kb6lSQ: \"x1hxa6v5\",\n kLWn49: \"x1iwmiir\",\n $$css: true\n },\n displayMedium: {\n kMv6JI: \"x1gi70vc\",\n kGuDYH: \"xlzzeuu\",\n k63SB2: \"xh9quta\",\n kb6lSQ: \"x148mp24\",\n kLWn49: \"xs5hucj\",\n $$css: true\n },\n displaySmall: {\n kMv6JI: \"x1eslvzl\",\n kGuDYH: \"x1g52wnc\",\n k63SB2: \"x1x664hp\",\n kb6lSQ: \"x1rnjaw4\",\n kLWn49: \"x92ldg4\",\n $$css: true\n },\n headlineLarge: {\n kMv6JI: \"x1vsmgmf\",\n kGuDYH: \"xh9c4x\",\n k63SB2: \"x1y15vu6\",\n kb6lSQ: \"xoqu3bc\",\n kLWn49: \"x10x9d6x\",\n $$css: true\n },\n headlineMedium: {\n kMv6JI: \"x18s19nt\",\n kGuDYH: \"xcohea8\",\n k63SB2: \"x1mtpwof\",\n kb6lSQ: \"x1kuyk5j\",\n kLWn49: \"xucx483\",\n $$css: true\n },\n headlineSmall: {\n kMv6JI: \"x1fz0g89\",\n kGuDYH: \"xkb6epg\",\n k63SB2: \"x76yrz5\",\n kb6lSQ: \"x1s1qhaa\",\n kLWn49: \"x1voeury\",\n $$css: true\n },\n labelLarge: {\n kMv6JI: \"xkknnkr\",\n kGuDYH: \"x14zspaw\",\n k63SB2: \"x1v9cpvo\",\n kb6lSQ: \"x155f00i\",\n kLWn49: \"x1lz68p4\",\n $$css: true\n },\n labelMedium: {\n kMv6JI: \"xp6n8n2\",\n kGuDYH: \"xkucv67\",\n k63SB2: \"x14hhc5u\",\n kb6lSQ: \"x12utw95\",\n kLWn49: \"xvzb9hk\",\n $$css: true\n },\n labelSmall: {\n kMv6JI: \"xs1w0rn\",\n kGuDYH: \"x1n35her\",\n k63SB2: \"x1su64ma\",\n kb6lSQ: \"xdlnduh\",\n kLWn49: \"xaomhve\",\n $$css: true\n },\n titleLarge: {\n kMv6JI: \"xdy5w5q\",\n kGuDYH: \"xete2z3\",\n k63SB2: \"x14ijtet\",\n kb6lSQ: \"xug9e1u\",\n kLWn49: \"x2divse\",\n $$css: true\n },\n titleMedium: {\n kMv6JI: \"x46eyla\",\n kGuDYH: \"x5ehqhr\",\n k63SB2: \"xrf6cl6\",\n kb6lSQ: \"x1g135zh\",\n kLWn49: \"x1ggk5va\",\n $$css: true\n },\n titleSmall: {\n kMv6JI: \"xrgwony\",\n kGuDYH: \"x12s497w\",\n k63SB2: \"x1szcwkt\",\n kb6lSQ: \"xo803ec\",\n kLWn49: \"xyl1r0q\",\n $$css: true\n }\n};\n\n// Tone is a separate axis from variant so the two compose freely — any of the\n// 15 scale styles in any of these colors. Kept to the roles text actually\n// takes on a surface; a consumer needing something outside this set uses\n// `inherit` and colors the parent, rather than this growing a case per role.\nconst tones = {\n default: {\n kMwMTN: \"x139gbkf\",\n $$css: true\n },\n error: {\n kMwMTN: \"x4ii1qj\",\n $$css: true\n },\n inherit: {\n kMwMTN: \"x1heor9g\",\n $$css: true\n },\n muted: {\n kMwMTN: \"x1w5n3ug\",\n $$css: true\n },\n negative: {\n kMwMTN: \"x12gp0t\",\n $$css: true\n },\n positive: {\n kMwMTN: \"x4no3js\",\n $$css: true\n },\n primary: {\n kMwMTN: \"x1mxwv8y\",\n $$css: true\n }\n};\ntype TextProps = useRender.ComponentProps<'span'> & {\n /**\n * Renders a `<p>` rather than the default `<span>`, for a block of prose\n * rather than a run of text inside one.\n *\n * The paragraph carries no margin of its own, the same as every other Text.\n * The space between two paragraphs belongs to whatever holds both of them,\n * which is where it can be a step of the spacing scale rather than a\n * browser default.\n *\n * Ignored when `render` is given, since that names the element outright.\n * @default false\n */\n block?: boolean;\n /**\n * The color role to render in. `inherit` takes the color of the nearest\n * colored ancestor instead of setting one.\n * @default 'default'\n */\n tone?: 'default' | 'error' | 'inherit' | 'muted' | 'negative' | 'positive' | 'primary';\n /**\n * Which style of the type scale to render.\n * @default 'bodyMedium'\n */\n variant?: 'bodyLarge' | 'bodyMedium' | 'bodySmall' | 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall';\n};\nfunction Text({\n block = false,\n render,\n tone = 'default',\n variant = 'bodyMedium',\n ...props\n}: TextProps) {\n return useRender({\n // Card switches its default element the same way, and the props type\n // stays the span's for the same reason: the two differ only in the ref's\n // element interface, and neither adds anything a call site reaches for.\n defaultTagName: block ? 'p' : 'span',\n props: {\n ...props,\n ...mergeStyles(stylex.props(styles.base, styles[variant], tones[tone]), props)\n },\n render\n });\n}\nexport type { TextProps };\nexport default Text;"],"mappings":";;;;AAmBA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,gBAAgB;EACd,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,eAAe;EACb,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,aAAa;EACX,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,aAAa;EACX,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;AACF;AAMA,MAAM,QAAQ;CACZ,SAAS;EACP,QAAQ;EACR,OAAO;CACT;CACA,OAAO;EACL,QAAQ;EACR,OAAO;CACT;CACA,SAAS;EACP,QAAQ;EACR,OAAO;CACT;CACA,OAAO;EACL,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;CACA,SAAS;EACP,QAAQ;EACR,OAAO;CACT;AACF;AA2BA,SAAS,KAAK,EACZ,QAAQ,OACR,QACA,OAAO,WACP,UAAU,cACV,GAAG,WACS;CACZ,OAAO,UAAU;EAIf,gBAAgB,QAAQ,MAAM;EAC9B,OAAO;GACL,GAAG;GACH,GAAG,YAAY,MAAa,OAAO,MAAM,OAAO,UAAU,MAAM,KAAK,GAAG,OAAK;EAC/E;EACA;CACF,CAAC;AACH"}
package/dist/styles.css CHANGED
@@ -155,10 +155,18 @@
155
155
  padding-inline: 0;
156
156
  }
157
157
 
158
+ .xaope02 {
159
+ padding-inline: 12px;
160
+ }
161
+
158
162
  .x1sdxjpb {
159
163
  padding-inline: 48px;
160
164
  }
161
165
 
166
+ .x30g6up {
167
+ padding-inline: var(--x-paddingInline);
168
+ }
169
+
162
170
  .xg0ps1x {
163
171
  padding-inline: var(--x196ey9q);
164
172
  }
@@ -175,10 +183,6 @@
175
183
  padding-inline: var(--x6borv4);
176
184
  }
177
185
 
178
- .ximhkdq {
179
- padding-inline: var(--xbmbo34);
180
- }
181
-
182
186
  .x6e1k4i {
183
187
  padding-inline: var(--xi3mssk);
184
188
  }
@@ -951,10 +955,6 @@
951
955
  transition-duration: .105s;
952
956
  }
953
957
 
954
- .xx6bhzk {
955
- transition-duration: .15s;
956
- }
957
-
958
958
  .xbg8nd6 {
959
959
  transition-duration: .375s;
960
960
  }
@@ -967,6 +967,10 @@
967
967
  transition-duration: var(--x12wc0xg);
968
968
  }
969
969
 
970
+ .xioksi3 {
971
+ transition-duration: var(--x1neymab);
972
+ }
973
+
970
974
  .x1eaenvl {
971
975
  transition-property: background-color, border-color, color;
972
976
  }
@@ -979,8 +983,8 @@
979
983
  transition-property: background-color, color;
980
984
  }
981
985
 
982
- .x15406qy {
983
- transition-property: background-color;
986
+ .x8u3r54 {
987
+ transition-property: background-color, min-block-size;
984
988
  }
985
989
 
986
990
  .xkdsq27 {
@@ -995,6 +999,10 @@
995
999
  transition-property: color;
996
1000
  }
997
1001
 
1002
+ .x1nxaspg {
1003
+ transition-property: font-size, letter-spacing, line-height;
1004
+ }
1005
+
998
1006
  .x19991ni {
999
1007
  transition-property: opacity;
1000
1008
  }
@@ -1003,6 +1011,10 @@
1003
1011
  transition-timing-function: linear;
1004
1012
  }
1005
1013
 
1014
+ .x1seskgd {
1015
+ transition-timing-function: var(--x1naowyp);
1016
+ }
1017
+
1006
1018
  .x15l9jch {
1007
1019
  transition-timing-function: var(--x77wm8g),var(--x1naowyp);
1008
1020
  }
@@ -1395,6 +1407,11 @@
1395
1407
  inherits: false
1396
1408
  }
1397
1409
 
1410
+ @property --x-paddingInline {
1411
+ syntax: "*";
1412
+ inherits: false
1413
+ }
1414
+
1398
1415
  @keyframes x18re5ia-B {
1399
1416
  from {
1400
1417
  opacity: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanso-labs/kanso-ui",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "A React component library built on Base UI primitives and styled with StyleX, with design tokens sourced from a single W3C DTCG file and compiled via Style Dictionary",
5
5
  "keywords": [
6
6
  "react",