@viraui/react 2026.6.3 → 2026.6.5
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 +1 -3
- package/dist/components/button/button-link.js +1 -0
- package/dist/components/chip/chip.js +1 -0
- package/dist/components/dialog/dialog.css +1 -1
- package/dist/components/menu/menu-link-item.js +1 -0
- package/dist/components/tabs/tabs-tab.js +1 -0
- package/dist/components/text/text.css +1 -1
- package/dist/components/text/text.d.ts +4 -2
- package/dist/components/text/text.js +1 -1
- package/dist/components/typewriter/typewriter.css +1 -1
- package/dist/preflight.css +1 -1
- package/dist/specs/PROTOCOL.md +4 -3
- package/dist/specs/activity-hints.xml +3 -1
- package/dist/specs/components/button/usage.xml +1 -1
- package/dist/specs/components/chip/usage.xml +1 -1
- package/dist/specs/components/grid/props.xml +2 -2
- package/dist/specs/components/grid/usage.xml +1 -1
- package/dist/specs/components/menu/usage.xml +1 -1
- package/dist/specs/components/stack/props.xml +2 -2
- package/dist/specs/components/stack/usage.xml +2 -1
- package/dist/specs/components/surface/props.xml +2 -2
- package/dist/specs/components/surface/usage.xml +1 -1
- package/dist/specs/components/text/patterns.xml +9 -0
- package/dist/specs/components/text/props.xml +6 -6
- package/dist/specs/components/text/usage.xml +3 -1
- package/dist/specs/components/title/props.xml +2 -2
- package/dist/specs/components/title/usage.xml +2 -1
- package/dist/specs/foundation/attributes.xml +5 -5
- package/dist/specs/foundation/modules.xml +2 -2
- package/dist/specs/index.xml +1 -1
- package/dist/specs/patterns/compose-layout-first.xml +1 -1
- package/dist/specs/patterns/page-chrome.xml +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<!-- PROJECT LOGO -->
|
|
2
2
|
<br />
|
|
3
3
|
<div align="center">
|
|
4
|
-
<
|
|
5
|
-
<img alt="ViraUI" src="https://viraui.dev/viraui-logo-midgray.png" height="30" draggable="false" />
|
|
6
|
-
</h3>
|
|
4
|
+
<h1 align="center">ViraUI</h1>
|
|
7
5
|
<br />
|
|
8
6
|
<h2 align="center" style="font-weight: 500;">
|
|
9
7
|
Ship real UI at the speed of thought.
|
|
@@ -17,6 +17,7 @@ var ButtonLink = ({ children, className, addon, addonPosition = "start", loading
|
|
|
17
17
|
...otherProps,
|
|
18
18
|
"aria-busy": loading || void 0,
|
|
19
19
|
className: clsx(button_module_default.Button, className),
|
|
20
|
+
"data-no-link-style": true,
|
|
20
21
|
"data-loading": loadingAttr,
|
|
21
22
|
"data-pill": pill ? "true" : "false",
|
|
22
23
|
"data-size": size,
|
|
@@ -35,6 +35,7 @@ var Chip = ({ addon, addonPosition = "start", children, className, ref, render,
|
|
|
35
35
|
...otherProps,
|
|
36
36
|
"data-grow": "false",
|
|
37
37
|
"data-shrink": "false",
|
|
38
|
+
"data-no-link-style": true,
|
|
38
39
|
children: content,
|
|
39
40
|
className: clsx(chip_module_default.Chip, className),
|
|
40
41
|
"data-addon-position": addon ? addonPosition : void 0,
|
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
);--__dialog-translate-y:calc(
|
|
12
12
|
var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y, 0px) - var(--__dialog-stack-peek-offset) -
|
|
13
13
|
var(--__dialog-stack-shrink) * var(--__dialog-stack-height)
|
|
14
|
-
);max-block-size:var(--vui-dialog-max-block-size,97dvh);outline:none;pointer-events:auto;touch-action:none;height:var(--drawer-height,auto);transform-origin:50% calc(100% - var(--__dialog-bleed));transition:opacity var(--duration-slow) var(--easing-entrance),transform var(--duration-slow) var(--easing-entrance),height var(--duration-slow) var(--easing-entrance);transform:translateY(var(--__dialog-translate-y)) scale(var(--__dialog-stack-scale));&[data-nested-drawer-open]{height:calc(var(--__dialog-stack-height) + var(--__dialog-bleed))}&[data-starting-style]{opacity:0;transform:translateY(100%)}&[data-ending-style]{transition-duration:calc(var(--drawer-swipe-strength, 1) * var(--duration-fast));transition-timing-function:var(--easing-exit)}&[data-ending-style][data-swipe-direction=down]{transform:translateY(100%)}&[data-nested-drawer-swiping]:not([data-ending-style]),&[data-swiping]:not([data-ending-style]){transition-duration:0s}@media (prefers-reduced-motion:reduce){&[data-ending-style][data-swipe-direction=down],&[data-starting-style]{opacity:1;transform:translateY(0)}}}.viraui__dialog-module__HandleShape_mEg8R{--__corner-shape:round;inline-size:var(--space-xs);block-size:var(--space-xs);background-color:oklab(from var(--global-contrast) l a b/.3);border-radius:var(--radius-full)}.viraui__dialog-module__Header_WnBly{touch-action:none;-webkit-user-select:none;user-select:none}.viraui__dialog-module__Body_9-UgN{min-block-size:0}.viraui__dialog-module__Content_r8lJc{min-block-size:0;overflow:auto;overscroll-behavior:contain;touch-action:auto;padding-block-end:calc(env(safe-area-inset-bottom) + var(--space-m));transition:opacity var(--duration-fast) var(--easing-standard),overflow 0s allow-discrete var(--duration-fast);.viraui__dialog-module__Popup_5qIyj[data-nested-drawer-open] &{
|
|
14
|
+
);max-block-size:var(--vui-dialog-max-block-size,97dvh);outline:none;pointer-events:auto;touch-action:none;height:var(--drawer-height,auto);transform-origin:50% calc(100% - var(--__dialog-bleed));transition:opacity var(--duration-slow) var(--easing-entrance),transform var(--duration-slow) var(--easing-entrance),height var(--duration-slow) var(--easing-entrance);transform:translateY(var(--__dialog-translate-y)) scale(var(--__dialog-stack-scale));&[data-nested-drawer-open]{height:calc(var(--__dialog-stack-height) + var(--__dialog-bleed))}&[data-starting-style]{opacity:0;transform:translateY(100%)}&[data-ending-style]{transition-duration:calc(var(--drawer-swipe-strength, 1) * var(--duration-fast));transition-timing-function:var(--easing-exit)}&[data-ending-style][data-swipe-direction=down]{transform:translateY(100%)}&[data-nested-drawer-swiping]:not([data-ending-style]),&[data-swiping]:not([data-ending-style]){transition-duration:0s}@media (prefers-reduced-motion:reduce){&[data-ending-style][data-swipe-direction=down],&[data-starting-style]{opacity:1;transform:translateY(0)}}}.viraui__dialog-module__HandleShape_mEg8R{--__corner-shape:round;inline-size:var(--space-xs);block-size:var(--space-xs);background-color:oklab(from var(--global-contrast) l a b/.3);border-radius:var(--radius-full)}.viraui__dialog-module__Header_WnBly{touch-action:none;-webkit-user-select:none;user-select:none}.viraui__dialog-module__Body_9-UgN{min-block-size:0}.viraui__dialog-module__Content_r8lJc{min-block-size:0;overflow:auto;overscroll-behavior:contain;touch-action:auto;padding-block-end:calc(env(safe-area-inset-bottom) + var(--space-m));transition:opacity var(--duration-fast) var(--easing-standard),overflow 0s allow-discrete var(--duration-fast);.viraui__dialog-module__Popup_5qIyj[data-ending-style] &,.viraui__dialog-module__Popup_5qIyj[data-nested-drawer-open] &,.viraui__dialog-module__Popup_5qIyj[data-starting-style] &{overflow:hidden;transition:opacity var(--duration-fast) var(--easing-standard),overflow 0s allow-discrete}.viraui__dialog-module__Popup_5qIyj[data-nested-drawer-open] &{opacity:0}.viraui__dialog-module__Popup_5qIyj[data-nested-drawer-open][data-nested-drawer-swiping] &{opacity:1;overflow:hidden}}.viraui__dialog-module__IndentBackground_OEfbh{position:absolute;inset:0}.viraui__dialog-module__Indent_qfiNW{--__dialog-indent-radius:calc(1rem * (1 - var(--drawer-swipe-progress)));--__dialog-indent-transition:calc(1 - clamp(0, calc(var(--drawer-swipe-progress) * 100000), 1));transition:transform var(--duration-normal) var(--easing-entrance),border-radius var(--duration-fast) var(--easing-entrance);transition-duration:calc(var(--duration-normal) * var(--__dialog-indent-transition)),calc(var(--duration-fast) * var(--__dialog-indent-transition));transform-origin:center top;will-change:transform;&[data-active]{transform:scale(calc(.98 + (.02 * var(--drawer-swipe-progress)))) translateY(calc(.5rem * (1 - var(--drawer-swipe-progress))));border-top-left-radius:var(--__dialog-indent-radius);border-top-right-radius:var(--__dialog-indent-radius)}}}@layer viraui.overrides{.viraui__dialog-module__Popup_5qIyj[data-nested-drawer-open]{overflow:hidden}.viraui__dialog-module__CloseButton_U-B1r.viraui__dialog-module__CloseButton_U-B1r{position:absolute;top:1rem;right:1rem}}
|
|
@@ -9,6 +9,7 @@ import { Menu } from "@base-ui/react";
|
|
|
9
9
|
var MenuLinkItem = ({ addon, children, className, note, sentiment, ...otherProps }) => /* @__PURE__ */ jsx(Menu.LinkItem, {
|
|
10
10
|
...otherProps,
|
|
11
11
|
className: clsx(menu_module_default.Item, className),
|
|
12
|
+
"data-no-link-style": true,
|
|
12
13
|
...sentiment && { "data-sentiment": sentiment },
|
|
13
14
|
render: /* @__PURE__ */ jsx(Surface, {
|
|
14
15
|
radius: "auto",
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
var(--__text-size-min),
|
|
3
3
|
calc(var(--__text-size-min) + (var(--__text-size-max) - var(--__text-size-min)) * var(--__text-fluid)),
|
|
4
4
|
var(--__text-size-max)
|
|
5
|
-
);--vui-text-font-family:var(--font-family-body);--vui-text-line-height:var(--font-leading-m);margin:0;max-inline-size:var(--vui-text-max-width);font-family:var(--vui-text-font-family);font-size:var(--vui-text-font-size);line-height:var(--vui-text-line-height);text-align:var(--vui-text-align);letter-spacing:var(--vui-text-letter-spacing);text-transform:var(--vui-text-text-transform);white-space:var(--vui-text-white-space);word-break:break-word;font-weight:var(--vui-text-font-weight);font-stretch:calc(var(--vui-text-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-text-font-weight),"wdth" var(--vui-text-font-width),"opsz" var(--vui-text-font-opsz);text-box:var(--vui-text-trim) var(--vui-text-trim-type);&[data-text-balanced=true]{text-wrap:balance}&[data-text-selection=false]{-webkit-user-select:none;user-select:none}&[data-text-has-padding=true]{padding:var(--vui-text-padding-block-start) var(--vui-text-padding-inline-end) var(--vui-text-padding-block-end) var(--vui-text-padding-inline-start)}&[data-text-family=mono]{--vui-text-font-family:var(--font-family-mono)}&[data-text-size="4xl"]{--__text-size-min:var(--font-scale-body-3xl);--__text-size-max:var(--font-scale-body-4xl)}&[data-text-size="3xl"]{--__text-size-min:var(--font-scale-body-2xl);--__text-size-max:var(--font-scale-body-3xl)}&[data-text-size="2xl"]{--__text-size-min:var(--font-scale-body-xl);--__text-size-max:var(--font-scale-body-2xl)}&[data-text-size=xl]{--__text-size-min:var(--font-scale-body-l);--__text-size-max:var(--font-scale-body-xl)}&[data-text-size=l]{--__text-size-min:var(--font-scale-body-m);--__text-size-max:var(--font-scale-body-l)}&[data-text-size=s]{--__text-size-max:var(--font-scale-body-s)}&[data-text-size=s],&[data-text-size=xs]{--__text-size-min:var(--font-scale-body-xs)}&[data-text-size=xs]{--__text-size-max:var(--font-scale-body-xs)}&[data-text-fluid=false]{--vui-text-font-size:var(--__text-size-max)}@container root style(--effect-fluid-typography: initial){--vui-text-font-size:var(--__text-size-max)}&[data-text-weight=light]{--vui-text-font-weight:200}&[data-text-weight=semibold]{--vui-text-font-weight:600}&[data-text-weight=bold]{--vui-text-font-weight:800}&[data-text-tone=primary]{color:var(--global-primary)}&[data-text-tone=muted]{color:var(--global-muted)}&[data-text-tone=interactive]{color:var(--global-interactive-text)}&[data-text-tone=positive]{color:var(--highlight-green)}&[data-text-tone=informative]{color:var(--highlight-blue)}&[data-text-tone=danger]{color:var(--highlight-red)}&[data-text-tone=warning]{color:var(--highlight-yellow)}&[data-text-has-literal-color=true]{color:var(--vui-text-literal-color)}&[data-text-line-height=none]{--vui-text-line-height:0}&[data-text-line-height=xs]{--vui-text-line-height:var(--font-leading-xs)}&[data-text-line-height=s]{--vui-text-line-height:var(--font-leading-s)}&[data-text-line-height=m]{--vui-text-line-height:var(--font-leading-m)}&[data-text-line-height=l]{--vui-text-line-height:var(--font-leading-l)}}}
|
|
5
|
+
);--vui-text-font-family:var(--font-family-body);--vui-text-line-height:var(--font-leading-m);margin:0;max-inline-size:var(--vui-text-max-width);font-family:var(--vui-text-font-family);font-size:var(--vui-text-font-size);line-height:var(--vui-text-line-height);text-align:var(--vui-text-align);letter-spacing:var(--vui-text-letter-spacing);text-transform:var(--vui-text-text-transform);white-space:var(--vui-text-white-space);word-break:break-word;font-weight:var(--vui-text-font-weight);font-stretch:calc(var(--vui-text-font-width) * 1%);font-optical-sizing:auto;font-variation-settings:"wght" var(--vui-text-font-weight),"wdth" var(--vui-text-font-width),"opsz" var(--vui-text-font-opsz);text-box:var(--vui-text-trim) var(--vui-text-trim-type);&[data-text-balanced=true]{text-wrap:balance}&[data-text-selection=false]{-webkit-user-select:none;user-select:none}&[data-text-has-padding=true]{padding:var(--vui-text-padding-block-start) var(--vui-text-padding-inline-end) var(--vui-text-padding-block-end) var(--vui-text-padding-inline-start)}&[data-text-family=mono]{--vui-text-font-family:var(--font-family-mono)}&[data-text-size="4xl"]{--__text-size-min:var(--font-scale-body-3xl);--__text-size-max:var(--font-scale-body-4xl)}&[data-text-size="3xl"]{--__text-size-min:var(--font-scale-body-2xl);--__text-size-max:var(--font-scale-body-3xl)}&[data-text-size="2xl"]{--__text-size-min:var(--font-scale-body-xl);--__text-size-max:var(--font-scale-body-2xl)}&[data-text-size=xl]{--__text-size-min:var(--font-scale-body-l);--__text-size-max:var(--font-scale-body-xl)}&[data-text-size=l]{--__text-size-min:var(--font-scale-body-m);--__text-size-max:var(--font-scale-body-l)}&[data-text-size=s]{--__text-size-max:var(--font-scale-body-s)}&[data-text-size=s],&[data-text-size=xs]{--__text-size-min:var(--font-scale-body-xs)}&[data-text-size=xs]{--__text-size-max:var(--font-scale-body-xs)}& .viraui__text-module__Text_bS8kv:not([data-text-size]){font-size:inherit}& .viraui__text-module__Text_bS8kv:not([data-text-line-height]){line-height:inherit}&[data-text-fluid=false]{--vui-text-font-size:var(--__text-size-max)}@container root style(--effect-fluid-typography: initial){--vui-text-font-size:var(--__text-size-max)}&[data-text-weight=light]{--vui-text-font-weight:200}&[data-text-weight=semibold]{--vui-text-font-weight:600}&[data-text-weight=bold]{--vui-text-font-weight:800}&[data-text-tone=primary]{color:var(--global-primary)}&[data-text-tone=muted]{color:var(--global-muted)}&[data-text-tone=interactive]{color:var(--global-interactive-text)}&[data-text-tone=positive]{color:var(--highlight-green)}&[data-text-tone=informative]{color:var(--highlight-blue)}&[data-text-tone=danger]{color:var(--highlight-red)}&[data-text-tone=warning]{color:var(--highlight-yellow)}&[data-text-has-literal-color=true]{color:var(--vui-text-literal-color)}&[data-text-line-height=none]{--vui-text-line-height:0}&[data-text-line-height=xs]{--vui-text-line-height:var(--font-leading-xs)}&[data-text-line-height=s]{--vui-text-line-height:var(--font-leading-s)}&[data-text-line-height=m]{--vui-text-line-height:var(--font-leading-m)}&[data-text-line-height=l]{--vui-text-line-height:var(--font-leading-l)}}}
|
|
@@ -27,8 +27,9 @@ export type TextProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
27
27
|
/**
|
|
28
28
|
* Selects a semantic body typography size from the active theme scale
|
|
29
29
|
* (`4xl` | `3xl` | `2xl` | `xl` | `l` | `m` | `s` | `xs`).
|
|
30
|
+
* Omit when nested in Text so font-size inherits the outer Text size.
|
|
30
31
|
*
|
|
31
|
-
* @default 'm'
|
|
32
|
+
* @default 'm' on root Text (CSS); inherit when nested and omitted
|
|
32
33
|
*/
|
|
33
34
|
size?: '4xl' | '3xl' | '2xl' | 'xl' | 'l' | 'm' | 's' | 'xs';
|
|
34
35
|
/**
|
|
@@ -60,8 +61,9 @@ export type TextProps = React.ComponentPropsWithoutRef<'span'> & {
|
|
|
60
61
|
color?: string;
|
|
61
62
|
/**
|
|
62
63
|
* Controls line height through foundation leading tokens.
|
|
64
|
+
* Omit when nested in Text so line-height inherits the outer Text.
|
|
63
65
|
*
|
|
64
|
-
* @default 'm'
|
|
66
|
+
* @default 'm' on root Text (CSS); inherit when nested and omitted
|
|
65
67
|
*/
|
|
66
68
|
lineHeight?: 'none' | 'xs' | 's' | 'm' | 'l';
|
|
67
69
|
/**
|
|
@@ -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 = "start", balanced = false, selection = true, className, color, family = "body", fluid = false, fontWidth, hPadding, lineHeight
|
|
7
|
+
var Text = ({ align = "start", balanced = false, selection = true, className, color, family = "body", fluid = false, fontWidth, hPadding, lineHeight, maxWidth, opticalSize, ref, render, size, style, tone, tracking, transform, trim = "both", trimType = "cap alphabetic", vPadding, weight = "regular", whiteSpace = "normal", ...otherProps }) => {
|
|
8
8
|
const hasPadding = Boolean(hPadding || vPadding);
|
|
9
9
|
const dynamicStyle = {
|
|
10
10
|
"--vui-text-align": align,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-typewriter-caret-color{syntax:"<color>";inherits:true;initial-value:currentcolor}.viraui__typewriter-module__Typewriter_R3sbb{position:relative;isolation:isolate}.viraui__typewriter-module__Ghost_R-Yy-{visibility:hidden;white-space:pre-wrap}.viraui__typewriter-module__Typed_00NZ8{white-space:pre-wrap}.viraui__typewriter-module__Typewriter_R3sbb[data-preserve-space]>.viraui__typewriter-module__Typed_00NZ8{position:absolute;inset-inline
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-typewriter-caret-color{syntax:"<color>";inherits:true;initial-value:currentcolor}.viraui__typewriter-module__Typewriter_R3sbb{position:relative;isolation:isolate}.viraui__typewriter-module__Typewriter_R3sbb[data-preserve-space]{display:flex}.viraui__typewriter-module__Ghost_R-Yy-{visibility:hidden;white-space:pre-wrap}.viraui__typewriter-module__Typed_00NZ8{white-space:pre-wrap}.viraui__typewriter-module__Typewriter_R3sbb[data-preserve-space]>.viraui__typewriter-module__Typed_00NZ8{position:absolute;inset-inline:0;inset-block-start:0}.viraui__typewriter-module__Caret_96Yfs{display:inline-block;vertical-align:initial;color:var(--vui-typewriter-caret-color);animation:viraui__typewriter-module__typewriter-caret-blink_yLCpV 1.1s step-start infinite;&[data-type=block]{inline-size:.55em;block-size:.95em;margin-inline-start:.04em;background-color:var(--vui-typewriter-caret-color);transform:translateY(.08em)}&[data-type=pipe]:before{content:"|"}&[data-type=underscore]:before{content:"_"}}.viraui__typewriter-module__A11y_B-rjF{position:absolute;inset-block-start:0;inset-inline-start:0;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;pointer-events:none}@keyframes viraui__typewriter-module__typewriter-caret-blink_yLCpV{50%{opacity:0}}}
|
package/dist/preflight.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.preflight{@property --scrollbar-thumb{syntax:"<color>";initial-value:#0000;inherits:true}@property --scrollbar-track{syntax:"<color>";initial-value:#0000;inherits:true}@property --outline-offset{syntax:"<length>";initial-value:2px;inherits:true}@property --outline-color{syntax:"<color>";initial-value:currentcolor;inherits:true}@property --outline{syntax:"*";initial-value:2px solid currentcolor;inherits:true}@property --is-light-mode{syntax:"<color>";initial-value:#000;inherits:true}@property --vira-flex-grow{syntax:"<number>";initial-value:1;inherits:false}@property --vira-flex-shrink{syntax:"<number>";initial-value:1;inherits:false}@property --__corner-shape{syntax:"*";inherits:false}}@layer viraui.preflight{:root{--font-scale-body-m:1rem;--font-scale-body-s:min(1rem, max(0.875rem, round(down, calc(1rem / pow(var(--font-body-typescale), 1)), 1px)));--font-scale-body-xs:min(
|
|
2
2
|
var(--font-scale-body-s),
|
|
3
3
|
max(0.75rem, round(down, calc(1rem / pow(var(--font-body-typescale), 2)), 1px))
|
|
4
|
-
);--font-scale-body-l:round(down, calc(1rem * pow(var(--font-body-typescale), 1)), 1px);--font-scale-body-xl:round(down, calc(1rem * pow(var(--font-body-typescale), 2)), 1px);--font-scale-body-2xl:round(down, calc(1rem * pow(var(--font-body-typescale), 3)), 1px);--font-scale-body-3xl:round(down, calc(1rem * pow(var(--font-body-typescale), 4)), 1px);--font-scale-body-4xl:round(down, calc(1rem * pow(var(--font-body-typescale), 5)), 1px);--font-scale-h6:round(down, calc(1rem * pow(var(--font-heading-typescale), 1)), 1px);--font-scale-h5:round(down, calc(1rem * pow(var(--font-heading-typescale), 2)), 1px);--font-scale-h4:round(down, calc(1rem * pow(var(--font-heading-typescale), 3)), 1px);--font-scale-h3:round(down, calc(1rem * pow(var(--font-heading-typescale), 4)), 1px);--font-scale-h2:round(down, calc(1rem * pow(var(--font-heading-typescale), 5)), 1px);--font-scale-h1:round(down, calc(1rem * pow(var(--font-heading-typescale), 6)), 1px);--font-scale-display:round(down, calc(1rem * pow(var(--font-heading-typescale), 8)), 1px)}}@layer viraui.preflight{html{--scrollbar-thumb:oklab(from var(--global-contrast) l a b / 0.3);--scrollbar-track:#0000;--outline-offset:2px;--outline-color:var(--global-outline);--outline:2px solid var(--outline-color);--is-light-mode:contrast-color(var(--global-background));font-size:var(--font-root-size);box-sizing:border-box;line-height:1.5;background-color:var(--global-background);color:var(--global-foreground);container:root/normal;scroll-behavior:smooth;@media (prefers-reduced-motion:reduce){scroll-behavior:auto}}*,:after,:before{margin:0;box-sizing:inherit;scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track);@container root not style(--effect-corner-shape: initial){corner-shape:var(--__corner-shape,var(--corner-shape))}@media (prefers-reduced-motion:reduce){animation:none!important;transition:none!important}}:focus-visible{outline:var(--outline);outline-offset:var(--outline-offset)}body{color:var(--global-foreground);margin:0;font-size:var(--font-scale-body-m);font-optical-sizing:auto;font-family:var(--font-family-body),system-ui,sans-serif;font-variation-settings:"wght" 400;font-variant-numeric:tabular-nums;@container root not style(--effect-holofoil: initial){&:after{content:"";position:fixed;inset:0;z-index:999;background-color:#99ffd8;pointer-events:none;mix-blend-mode:multiply;contain:strict;isolation:isolate;opacity:25%;background-image:radial-gradient(at 79% 56%,#70ade6 0,#0000 50%),radial-gradient(at 61% 80%,#ee909e 0,#0000 50%),radial-gradient(at 0 63%,#fdbc68 0,#0000 50%),radial-gradient(at 6% 50%,#e5cb8a 0,#0000 50%),radial-gradient(at 23% 10%,#e5db8f 0,#0000 50%),radial-gradient(at 36% 91%,#a0eea8 0,#0000 50%),radial-gradient(at 2% 80%,#e3a47d 0,#0000 50%);@container root style(--is-light-mode: white){mix-blend-mode:color;opacity:15%}}}}::selection{background-color:var(--global-primary);color:contrast-color(var(--global-primary));text-shadow:none}img{display:block;inline-size:100%;max-inline-size:100%}svg{display:inline-flex;vertical-align:middle;&:not([fill]){fill:currentcolor}}ol ul,ul ol{font-size:medium}table{border-spacing:0;inline-size:100%;border-collapse:collapse}table[data-layout-fixed]{table-layout:fixed}table thead{box-shadow:inset 0 -3px 0 var(--base-2)}table[data-table-separators] tr+tr{border-top:1px solid var(--base-2)}table th{white-space:nowrap;font-variation-settings:"wght" 800}table :is(td,th){border:none;text-align:left;font-size:medium;padding:calc(var(--space-xs) + var(--space-2xs)) var(--space-s)}button,fieldset{padding:0;margin:0;border:0}button{appearance:none;background:none;font-size:inherit;font-family:inherit;display:inline-flex;cursor:pointer;color:inherit}}@layer viraui.preflight{@container root not style(--effect-shadows: initial){[data-elevation]{--shadow-direction:1;--shadow-opacity-factor:0.16;--extra-shadow:0 0 0 1px #0000;--shadow-color:oklch(0% 0 0);transition-property:box-shadow;transition-duration:var(--elevation-transition-duration,.16s);transition-timing-function:var(--elevation-transition-easing,ease)}[data-elevation]:where([data-elevation-direction=bottom],[data-elevation-direction=right]){--shadow-direction:1}[data-elevation]:where([data-elevation-direction=top],[data-elevation-direction=left]){--shadow-direction:-1}[data-elevation-hover="0"]:hover,[data-elevation="0"]{--y1:0px;--x1:0px;box-shadow:calc(var(--x1) * var(--shadow-direction)) calc(var(--y1) * var(--shadow-direction)) 0 oklch(from var(--shadow-color) l c h/var(--shadow-opacity-factor)),0 0 0 color-mix(in oklch,var(--shadow-color),#0000 calc(94% - var(--shadow-opacity-factor))),0 0 0 oklch(from var(--shadow-color) l c h/var(--shadow-opacity-factor)),var(--extra-shadow)}[data-elevation-hover="1"]:hover,[data-elevation-hover="2"]:hover,[data-elevation-hover="3"]:hover,[data-elevation-hover="4"]:hover,[data-elevation="1"],[data-elevation="2"],[data-elevation="3"],[data-elevation="4"]{box-shadow:calc(var(--x1) * var(--shadow-direction)) calc(var(--y1) * var(--shadow-direction)) var(--shadow-blur-1) oklab(from var(--shadow-color) l a b/.1),calc(var(--x2) * var(--shadow-direction)) calc(var(--y2) * var(--shadow-direction)) var(--shadow-blur-2) oklab(from var(--shadow-color) l a b/.08),calc(var(--x3) * var(--shadow-direction)) calc(var(--y3) * var(--shadow-direction)) var(--shadow-blur-3) oklab(from var(--shadow-color) l a b/.06),calc(var(--x4) * var(--shadow-direction)) calc(var(--y4) * var(--shadow-direction)) var(--shadow-blur-4) oklab(from var(--shadow-color) l a b/.04),calc(var(--x5) * var(--shadow-direction)) calc(var(--y5) * var(--shadow-direction)) var(--shadow-blur-5) oklab(from var(--shadow-color) l a b/.02),var(--extra-shadow)}[data-elevation-hover="1"]:hover,[data-elevation="1"]{--y1:0px;--y2:1px;--y3:1px;--y4:3px;--y5:4px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:0px;--shadow-blur-2:1px;--shadow-blur-3:1px;--shadow-blur-4:3px;--shadow-blur-5:4px}[data-elevation-hover="1"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="1"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:0px;--x2:1px;--x3:1px;--x4:3px;--x5:4px}[data-elevation-hover="2"]:hover,[data-elevation="2"]{--y1:0px;--y2:1px;--y3:3px;--y4:5px;--y5:8px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:0px;--shadow-blur-2:1px;--shadow-blur-3:3px;--shadow-blur-4:5px;--shadow-blur-5:8px}[data-elevation-hover="2"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="2"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:0px;--x2:1px;--x3:3px;--x4:5px;--x5:8px}[data-elevation-hover="3"]:hover,[data-elevation="3"]{--y1:1px;--y2:3px;--y3:6px;--y4:10px;--y5:16px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:1px;--shadow-blur-2:3px;--shadow-blur-3:6px;--shadow-blur-4:10px;--shadow-blur-5:16px}[data-elevation-hover="3"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="3"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:1px;--x2:3px;--x3:6px;--x4:10px;--x5:16px}[data-elevation-hover="4"]:hover,[data-elevation="4"]{--y1:1px;--y2:5px;--y3:12px;--y4:20px;--y5:32px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:1px;--shadow-blur-2:5px;--shadow-blur-3:12px;--shadow-blur-4:20px;--shadow-blur-5:32px}[data-elevation-hover="4"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="4"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:1px;--x2:5px;--x3:12px;--x4:20px;--x5:32px}}}@layer viraui.preflight{[data-grow=true]{--vira-flex-grow:1;flex-grow:1}[data-grow=false]{--vira-flex-grow:0;flex-grow:0}[data-shrink=true]{--vira-flex-shrink:1;flex-shrink:1}[data-shrink=false]{--vira-flex-shrink:0;flex-shrink:0}}@layer viraui.preflight{:where(p){line-height:1.5;min-block-size:.01vh;font-size:var(--font-scale-body-m)}small{font-size:.7em}b,strong{font-variation-settings:"wght" 700}:where(a:any-link):not([data-no-link-style]){text-decoration:none;color:var(--global-interactive-text);word-break:break-word;&:not([class]){background:linear-gradient(currentcolor,currentcolor) no-repeat 0 100%;background-size:0 1px;transition:background-size var(--duration-fast) var(--easing-entrance);&:focus,&:hover{background-size:100% 1px}}}del{padding:0 .4em;margin:0 .2em;background-color:oklab(from var(--highlight-red) l a b/12%);color:var(--highlight-red);box-shadow:0 0 0 1px oklab(from var(--highlight-red) l a b/50%);border-radius:var(--radius-s)}ins{background-color:oklab(from var(--highlight-green) l a b/15%);color:var(--highlight-green);box-shadow:0 0 0 1px oklab(from var(--highlight-green) l a b/50%)}ins,mark{padding:0 .4em;margin:0 .2em;text-decoration:none;border-radius:var(--radius-s)}mark{background-color:oklab(from var(--highlight-yellow) l a b/15%);color:var(--highlight-yellow);box-shadow:0 0 0 1px oklab(from var(--highlight-yellow) l a b/50%)}address{font-style:normal;line-height:inherit}dt{font-variation-settings:"wght" 600}dd{margin:0;padding:0}ul{font-size:medium;padding-left:1rem;& li::marker{font-size:inherit;text-align:right;font-variation-settings:"wght" 400;min-inline-size:var(--space-xl);color:oklab(from currentColor l a b/80%)}}ol{font-size:medium;& li::marker{font-size:inherit;font-variation-settings:"wght" 400;min-inline-size:var(--space-xl);font-variant-numeric:tabular-nums;white-space:nowrap;color:oklab(from currentColor l a b/80%)}}kbd{padding:.2em .4em;border-radius:var(--radius-s);font-variation-settings:"wght" 600;box-shadow:var(--global-contrast);background:var(--global-contrast)}abbr{text-decoration:underline dashed var(--global-primary)}label,legend{font-size:inherit}code{font-size:1em;font-family:var(--font-family-mono);&:not(:where(pre &)){background-color:var(--global-contrast);border-radius:var(--radius-s);box-shadow:var(--global-contrast);padding:.2em .4em}}pre code{box-shadow:none}s{text-decoration:line-through;color:var(--global-disabled-foreground);text-decoration-color:oklab(from var(--global-foreground) l a b/50%)}:root{color-scheme:light dark}[data-mode=dark]{--is-light-mode:#fff;color:var(--global-foreground);color-scheme:dark}[data-mode=light]{--is-light-mode:#000;color:var(--global-foreground);color-scheme:light}}@layer viraui.preflight{[data-mode=inverted]{color:var(--global-foreground);@container root style(--is-light-mode: black){color-scheme:dark}@container root style(--is-light-mode: white){color-scheme:light}}}@layer viraui.preflight{@container root not style(--effect-vibrancy: initial){[data-vibrant]{backdrop-filter:brightness(var(--vibrancy-brightness)) blur(calc(var(--vibrancy-blur) * 1px)) saturate(150%);@container root style(--is-light-mode: black){backdrop-filter:brightness(100%) blur(calc(var(--vibrancy-blur) * 1px)) saturate(150%)}}}}
|
|
4
|
+
);--font-scale-body-l:round(down, calc(1rem * pow(var(--font-body-typescale), 1)), 1px);--font-scale-body-xl:round(down, calc(1rem * pow(var(--font-body-typescale), 2)), 1px);--font-scale-body-2xl:round(down, calc(1rem * pow(var(--font-body-typescale), 3)), 1px);--font-scale-body-3xl:round(down, calc(1rem * pow(var(--font-body-typescale), 4)), 1px);--font-scale-body-4xl:round(down, calc(1rem * pow(var(--font-body-typescale), 5)), 1px);--font-scale-h6:round(down, calc(1rem * pow(var(--font-heading-typescale), 1)), 1px);--font-scale-h5:round(down, calc(1rem * pow(var(--font-heading-typescale), 2)), 1px);--font-scale-h4:round(down, calc(1rem * pow(var(--font-heading-typescale), 3)), 1px);--font-scale-h3:round(down, calc(1rem * pow(var(--font-heading-typescale), 4)), 1px);--font-scale-h2:round(down, calc(1rem * pow(var(--font-heading-typescale), 5)), 1px);--font-scale-h1:round(down, calc(1rem * pow(var(--font-heading-typescale), 6)), 1px);--font-scale-display:round(down, calc(1rem * pow(var(--font-heading-typescale), 8)), 1px)}}@layer viraui.preflight{html{--scrollbar-thumb:oklab(from var(--global-contrast) l a b / 0.3);--scrollbar-track:#0000;--outline-offset:2px;--outline-color:var(--global-outline);--outline:2px solid var(--outline-color);--is-light-mode:contrast-color(var(--global-background));font-size:var(--font-root-size);box-sizing:border-box;line-height:1.5;background-color:var(--global-background);color:var(--global-foreground);container:root/normal;scroll-behavior:smooth;@media (prefers-reduced-motion:reduce){scroll-behavior:auto}}*,:after,:before{margin:0;box-sizing:inherit;scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track);@container root not style(--effect-corner-shape: initial){corner-shape:var(--__corner-shape,var(--corner-shape))}@media (prefers-reduced-motion:reduce){animation:none!important;transition:none!important}}:focus-visible{outline:var(--outline);outline-offset:var(--outline-offset)}body{color:var(--global-foreground);margin:0;font-size:var(--font-scale-body-m);font-optical-sizing:auto;font-family:var(--font-family-body),system-ui,sans-serif;font-variation-settings:"wght" 400;font-variant-numeric:tabular-nums;@container root not style(--effect-holofoil: initial){&:after{content:"";position:fixed;inset:0;z-index:999;background-color:#99ffd8;pointer-events:none;mix-blend-mode:multiply;contain:strict;isolation:isolate;opacity:25%;background-image:radial-gradient(at 79% 56%,#70ade6 0,#0000 50%),radial-gradient(at 61% 80%,#ee909e 0,#0000 50%),radial-gradient(at 0 63%,#fdbc68 0,#0000 50%),radial-gradient(at 6% 50%,#e5cb8a 0,#0000 50%),radial-gradient(at 23% 10%,#e5db8f 0,#0000 50%),radial-gradient(at 36% 91%,#a0eea8 0,#0000 50%),radial-gradient(at 2% 80%,#e3a47d 0,#0000 50%);@container root style(--is-light-mode: white){mix-blend-mode:color;opacity:15%}}}}::selection{background-color:var(--global-primary);color:contrast-color(var(--global-primary));text-shadow:none}img{display:block;inline-size:100%;max-inline-size:100%}svg{display:inline-flex;vertical-align:middle;&:not([fill]){fill:currentcolor}}ol ul,ul ol{font-size:medium}table{border-spacing:0;inline-size:100%;border-collapse:collapse}table[data-layout-fixed]{table-layout:fixed}table thead{box-shadow:inset 0 -3px 0 var(--base-2)}table[data-table-separators] tr+tr{border-top:1px solid var(--base-2)}table th{white-space:nowrap;font-variation-settings:"wght" 800}table :is(td,th){border:none;text-align:left;font-size:medium;padding:calc(var(--space-xs) + var(--space-2xs)) var(--space-s)}button,fieldset{padding:0;margin:0;border:0}button{appearance:none;background:none;font-size:inherit;font-family:inherit;display:inline-flex;cursor:pointer;color:inherit}}@layer viraui.preflight{@container root not style(--effect-shadows: initial){[data-elevation]{--shadow-direction:1;--shadow-opacity-factor:0.16;--extra-shadow:0 0 0 1px #0000;--shadow-color:oklch(0% 0 0);transition-property:box-shadow;transition-duration:var(--elevation-transition-duration,.16s);transition-timing-function:var(--elevation-transition-easing,ease)}[data-elevation]:where([data-elevation-direction=bottom],[data-elevation-direction=right]){--shadow-direction:1}[data-elevation]:where([data-elevation-direction=top],[data-elevation-direction=left]){--shadow-direction:-1}[data-elevation-hover="0"]:hover,[data-elevation="0"]{--y1:0px;--x1:0px;box-shadow:calc(var(--x1) * var(--shadow-direction)) calc(var(--y1) * var(--shadow-direction)) 0 oklch(from var(--shadow-color) l c h/var(--shadow-opacity-factor)),0 0 0 color-mix(in oklch,var(--shadow-color),#0000 calc(94% - var(--shadow-opacity-factor))),0 0 0 oklch(from var(--shadow-color) l c h/var(--shadow-opacity-factor)),var(--extra-shadow)}[data-elevation-hover="1"]:hover,[data-elevation-hover="2"]:hover,[data-elevation-hover="3"]:hover,[data-elevation-hover="4"]:hover,[data-elevation="1"],[data-elevation="2"],[data-elevation="3"],[data-elevation="4"]{box-shadow:calc(var(--x1) * var(--shadow-direction)) calc(var(--y1) * var(--shadow-direction)) var(--shadow-blur-1) oklab(from var(--shadow-color) l a b/.1),calc(var(--x2) * var(--shadow-direction)) calc(var(--y2) * var(--shadow-direction)) var(--shadow-blur-2) oklab(from var(--shadow-color) l a b/.08),calc(var(--x3) * var(--shadow-direction)) calc(var(--y3) * var(--shadow-direction)) var(--shadow-blur-3) oklab(from var(--shadow-color) l a b/.06),calc(var(--x4) * var(--shadow-direction)) calc(var(--y4) * var(--shadow-direction)) var(--shadow-blur-4) oklab(from var(--shadow-color) l a b/.04),calc(var(--x5) * var(--shadow-direction)) calc(var(--y5) * var(--shadow-direction)) var(--shadow-blur-5) oklab(from var(--shadow-color) l a b/.02),var(--extra-shadow)}[data-elevation-hover="1"]:hover,[data-elevation="1"]{--y1:0px;--y2:1px;--y3:1px;--y4:3px;--y5:4px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:0px;--shadow-blur-2:1px;--shadow-blur-3:1px;--shadow-blur-4:3px;--shadow-blur-5:4px}[data-elevation-hover="1"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="1"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:0px;--x2:1px;--x3:1px;--x4:3px;--x5:4px}[data-elevation-hover="2"]:hover,[data-elevation="2"]{--y1:0px;--y2:1px;--y3:3px;--y4:5px;--y5:8px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:0px;--shadow-blur-2:1px;--shadow-blur-3:3px;--shadow-blur-4:5px;--shadow-blur-5:8px}[data-elevation-hover="2"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="2"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:0px;--x2:1px;--x3:3px;--x4:5px;--x5:8px}[data-elevation-hover="3"]:hover,[data-elevation="3"]{--y1:1px;--y2:3px;--y3:6px;--y4:10px;--y5:16px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:1px;--shadow-blur-2:3px;--shadow-blur-3:6px;--shadow-blur-4:10px;--shadow-blur-5:16px}[data-elevation-hover="3"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="3"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:1px;--x2:3px;--x3:6px;--x4:10px;--x5:16px}[data-elevation-hover="4"]:hover,[data-elevation="4"]{--y1:1px;--y2:5px;--y3:12px;--y4:20px;--y5:32px;--x1:0px;--x2:0px;--x3:0px;--x4:0px;--x5:0px;--shadow-blur-1:1px;--shadow-blur-2:5px;--shadow-blur-3:12px;--shadow-blur-4:20px;--shadow-blur-5:32px}[data-elevation-hover="4"]:where([data-elevation-direction=left],[data-elevation-direction=right]):hover,[data-elevation="4"]:where([data-elevation-direction=left],[data-elevation-direction=right]){--y1:0px;--y2:0px;--y3:0px;--y4:0px;--y5:0px;--x1:1px;--x2:5px;--x3:12px;--x4:20px;--x5:32px}}}@layer viraui.preflight{[data-grow=true]{--vira-flex-grow:1;flex-grow:1}[data-grow=false]{--vira-flex-grow:0;flex-grow:0}[data-shrink=true]{--vira-flex-shrink:1;flex-shrink:1}[data-shrink=false]{--vira-flex-shrink:0;flex-shrink:0}}@layer viraui.preflight{:where(p){line-height:1.5;min-block-size:.01vh;font-size:var(--font-scale-body-m)}small{font-size:.7em}b,strong{font-variation-settings:"wght" 700}:where(a:any-link):not([data-no-link-style]){text-decoration:none;color:var(--global-interactive-text);word-break:break-word;background:linear-gradient(currentcolor,currentcolor) no-repeat 0 100%;background-size:0 1px;transition:background-size var(--duration-fast) var(--easing-entrance);&:focus,&:hover{background-size:100% 1px}}del{padding:0 .4em;margin:0 .2em;background-color:oklab(from var(--highlight-red) l a b/12%);color:var(--highlight-red);box-shadow:0 0 0 1px oklab(from var(--highlight-red) l a b/50%);border-radius:var(--radius-s)}ins{background-color:oklab(from var(--highlight-green) l a b/15%);color:var(--highlight-green);box-shadow:0 0 0 1px oklab(from var(--highlight-green) l a b/50%)}ins,mark{padding:0 .4em;margin:0 .2em;text-decoration:none;border-radius:var(--radius-s)}mark{background-color:oklab(from var(--highlight-yellow) l a b/15%);color:var(--highlight-yellow);box-shadow:0 0 0 1px oklab(from var(--highlight-yellow) l a b/50%)}address{font-style:normal;line-height:inherit}dt{font-variation-settings:"wght" 600}dd{margin:0;padding:0}ul{font-size:medium;padding-left:1rem;& li::marker{font-size:inherit;text-align:right;font-variation-settings:"wght" 400;min-inline-size:var(--space-xl);color:oklab(from currentColor l a b/80%)}}ol{font-size:medium;& li::marker{font-size:inherit;font-variation-settings:"wght" 400;min-inline-size:var(--space-xl);font-variant-numeric:tabular-nums;white-space:nowrap;color:oklab(from currentColor l a b/80%)}}kbd{padding:.2em .4em;border-radius:var(--radius-s);font-variation-settings:"wght" 600;box-shadow:var(--global-contrast);background:var(--global-contrast)}abbr{text-decoration:underline dashed var(--global-primary)}label,legend{font-size:inherit}code{font-size:1em;font-family:var(--font-family-mono);&:not(:where(pre &)){background-color:var(--global-contrast);border-radius:var(--radius-s);box-shadow:var(--global-contrast);padding:.2em .4em}}pre code{box-shadow:none}s{text-decoration:line-through;color:var(--global-disabled-foreground);text-decoration-color:oklab(from var(--global-foreground) l a b/50%)}:root{color-scheme:light dark}[data-mode=dark]{--is-light-mode:#fff;color:var(--global-foreground);color-scheme:dark}[data-mode=light]{--is-light-mode:#000;color:var(--global-foreground);color-scheme:light}}@layer viraui.preflight{[data-mode=inverted]{color:var(--global-foreground);@container root style(--is-light-mode: black){color-scheme:dark}@container root style(--is-light-mode: white){color-scheme:light}}}@layer viraui.preflight{@container root not style(--effect-vibrancy: initial){[data-vibrant]{backdrop-filter:brightness(var(--vibrancy-brightness)) blur(calc(var(--vibrancy-blur) * 1px)) saturate(150%);@container root style(--is-light-mode: black){backdrop-filter:brightness(100%) blur(calc(var(--vibrancy-blur) * 1px)) saturate(150%)}}}}
|
package/dist/specs/PROTOCOL.md
CHANGED
|
@@ -44,9 +44,10 @@ Prefer opening via package export / that `dist/specs/` tree. The `version` attri
|
|
|
44
44
|
Consumer agents build screens **with** ViraUI — public parts, compounds, and props from the units — not **around** it with immediate CSS overrides.
|
|
45
45
|
|
|
46
46
|
1. Index → activity load → open `meta` / `props` / `usage` for every Vira surface you touch **before** JSX that invents classNames or a CSS module targeting those roots.
|
|
47
|
-
2. Prefer another Vira part or public prop over custom CSS (layout → `Stack`/`Grid`/`Masonry`/`Bleed`/`Surface`; fill → `fullWidth` / `expandChildren`; type → `Text`/`Title` props; chrome → `Surface` / `Elevator`).
|
|
48
|
-
3.
|
|
49
|
-
4.
|
|
47
|
+
2. Prefer another Vira part or public prop over custom CSS (layout → `Stack`/`Grid`/`Masonry`/`Bleed`/`Surface`; fill → `fullWidth` / `expandChildren`; one-sided inset → `hPadding`/`vPadding` `[start, end]` tuples — not `paddingBlock*`/`paddingInline*` style; type → `Text`/`Title` props; chrome → `Surface` / `Elevator`).
|
|
48
|
+
3. **Omit `render` when it restates the component’s default root.** Confirm the default in that surface’s `props`/`usage` (e.g. Stack → `div`). Do **not** write `render={<div />}` on Stack, or any other `render={<SameDefault />}` noop. Use `render` only to change the host (semantic tag, `Surface`, another component).
|
|
49
|
+
4. Custom CSS defaults to **app chrome the system does not own** (page backdrop image, document scrollport, one-off marketing layout). It is **not** a license to reshape Vira control geometry, reinvent owned props, or “fix” controls that already look correct under theme + preflight — **unless the consumer expressly requests** that override / personalization. A future override layer will own formal customization hooks; until then, still open `props`/`usage` first and apply only the asked CSS.
|
|
50
|
+
5. If `usage.xml` lists a **dont** against the CSS you are about to write and the consumer did **not** expressly override that guidance — stop. Do not ship it.
|
|
50
51
|
|
|
51
52
|
Opening a CSS module on a Vira root without having opened that surface’s `props.xml` + `usage.xml` is a protocol defect. Unsolicited reshape CSS (no consumer ask) is the same defect.
|
|
52
53
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<forbid>Opening every component props.xml in the package</forbid>
|
|
21
21
|
<forbid>Writing unsolicited className / CSS module / utility styles on a Vira root before opening that surface props.xml and usage.xml</forbid>
|
|
22
22
|
<forbid>Treating Vira controls as raw HTML to reshape without an express consumer request for override / personalization — compose with public parts and props first</forbid>
|
|
23
|
+
<forbid>Passing render that restates the component default root (e.g. Stack render={<div />}; confirm default in props/usage) — omit render unless the host changes</forbid>
|
|
23
24
|
</activity>
|
|
24
25
|
<activity id="foundation-attrs">
|
|
25
26
|
<description>Mode, vibrancy, elevation, grow/shrink hooks.</description>
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
<step unit="usage" note="confirm stack/surface/grid (etc.) do/dont after pattern" />
|
|
64
65
|
<step unit="props" note="verify spacing / render / fullWidth names" />
|
|
65
66
|
</load>
|
|
66
|
-
<forbid>Inventing display:flex / display:grid / CSS gap/padding/border chrome when Stack, Grid, Masonry, Bleed, or Surface cover it</forbid>
|
|
67
|
+
<forbid>Inventing display:flex / display:grid / CSS gap/padding/paddingBlock*/paddingInline*/border chrome when Stack, Grid, Masonry, Bleed, or Surface cover it — one-sided inset → hPadding/vPadding [start, end] tuples</forbid>
|
|
68
|
+
<forbid>Passing render={<div />} (or any render that matches the component default root) — omit render; use it only for a different host</forbid>
|
|
67
69
|
</activity>
|
|
68
70
|
</activity_hints>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<do>Full-bleed or equal-width Buttons: wrap in Stack with `fullWidth` and/or `expandChildren` — never per-Button width CSS</do>
|
|
8
8
|
<do>Dialog / sheet action strips: Stack row of Button + ButtonLink; Cancel via `Dialog.Close render={<Button variant="secondary" />}`</do>
|
|
9
9
|
<do>Primary Chip addons inside a primary Button — set `data-mode="inverted"` on the Chip so its fill contrasts</do>
|
|
10
|
-
<do>Navigation that should look like Button — use ButtonLink (framework Link via `render`)</do>
|
|
10
|
+
<do>Navigation that should look like Button — use ButtonLink (framework Link via `render`; sets `data-no-link-style` so preflight underline does not fight button paint)</do>
|
|
11
11
|
<dont>Using Button without children as icon-only — prefer IconButton</dont>
|
|
12
12
|
<dont>Using Button with `render={<a />}` for links — Base UI enforces button semantics; use ButtonLink</dont>
|
|
13
13
|
<dont>Inventing `width: 100%` / `inline-size: 100%` / `w-full` / `.fullWidth` / size-fill `className` (e.g. `.Submit`) on Button when a parent Stack can own the fill</dont>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<do>Color-coded status via highlight `variant` values (`green`, `red`, `yellow`, …) or `primary` / `secondary` / `outline`</do>
|
|
6
6
|
<do>Primary Chip as Button `addon` — set `data-mode="inverted"` on the Chip when the Button is primary so fills contrast</do>
|
|
7
7
|
<do>Equal-width Chip clusters: wrap in Stack with `fullWidth` and/or `expandChildren` — never per-Chip width CSS</do>
|
|
8
|
-
<do>Polymorphic label root via `render` (default `div`)</do>
|
|
8
|
+
<do>Polymorphic label root via `render` (default `div`; Chip sets `data-no-link-style` so Chip-as-`<a>` keeps chip chrome)</do>
|
|
9
9
|
<dont>Using Chip as a button replacement for clickable actions — use Button or IconButton</dont>
|
|
10
10
|
<dont>Expecting `addonPosition` to have any effect when `addon` is omitted</dont>
|
|
11
11
|
<dont>Introducing custom size variants instead of the fixed compact height</dont>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<description>Controls `repeat(auto-fill|auto-fit, …)` when `columns` is unset. Pass `false` to skip the filling data attribute.</description>
|
|
25
25
|
</prop>
|
|
26
26
|
<prop name="hPadding" kind="react">
|
|
27
|
-
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
27
|
+
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
|
|
28
28
|
</prop>
|
|
29
29
|
<prop name="Item.column" kind="react">
|
|
30
30
|
<description>Sets explicit `grid-column` placement via `--vui-grid-item-column`.</description>
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
<description>Fixes the number of row tracks via `--vui-grid-rows`.</description>
|
|
64
64
|
</prop>
|
|
65
65
|
<prop name="vPadding" kind="react">
|
|
66
|
-
<description>Sets block padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
66
|
+
<description>Sets block padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset (e.g. bottom only `vPadding={[0, 'l']}`) — never `style`/`paddingBlock*` CSS when this prop covers it.</description>
|
|
67
67
|
</prop>
|
|
68
68
|
<prop name="column-gap" kind="css">
|
|
69
69
|
<description>Grid column gap on the root.</description>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<do>Token-spaced grids with optional axis padding without custom grid CSS</do>
|
|
9
9
|
<dont>Using Masonry when you need explicit row/column placement or full-width/full-height spans</dont>
|
|
10
10
|
<dont>Re-declaring `grid-template` and `gap` in app CSS when Grid can own the layout</dont>
|
|
11
|
-
<dont>Inventing CSS `gap` / `padding`
|
|
11
|
+
<dont>Inventing CSS `gap` / `padding` / `paddingBlock*` / `paddingInline*` / side paddings (`style` or className) when public spacing props cover it — one-sided inset uses `hPadding`/`vPadding` `[start, end]` tuples</dont>
|
|
12
12
|
<dont>Setting both `fullWidth` and `column` (or `fullHeight` and `row`) and expecting the explicit placement to win — full-* data attrs override</dont>
|
|
13
13
|
<dont>Using Grid for simple one-axis flex spacing — prefer Stack</dont>
|
|
14
14
|
</usage>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<usage surface="menu">
|
|
3
3
|
<do>Action menus anchored to Button or IconButton — not page-blocking flows (Dialog) or rich free-layout panels (Popover)</do>
|
|
4
4
|
<do>Nested command lists with `Menu.CheckboxItem` / `Menu.RadioGroup`+`Menu.RadioItem` (stay open on toggle by default) and `Menu.Submenu` + nested `Menu.Popup`</do>
|
|
5
|
-
<do>Mix `Menu.LinkItem` with action `Menu.Item` rows; group with `Menu.Group` / `Menu.Separator
|
|
5
|
+
<do>Mix `Menu.LinkItem` with action `Menu.Item` rows; group with `Menu.Group` / `Menu.Separator` (`LinkItem` sets `data-no-link-style`)</do>
|
|
6
6
|
<do>Account / user chip: `Menu.Trigger` as Surface/Stack (Avatar + Text) opening `Menu.Popup` with static profile header plus Items</do>
|
|
7
7
|
<do>Detached or multi-trigger menus via `Menu.createHandle`</do>
|
|
8
8
|
<do>Custom portal host (iframe preview, shadow root, or app overlay root) via `container` on `Menu.Popup`</do>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<description>Aligns content on the horizontal axis from the consumer point of view.</description>
|
|
35
35
|
</prop>
|
|
36
36
|
<prop name="hPadding" kind="react">
|
|
37
|
-
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
37
|
+
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
|
|
38
38
|
</prop>
|
|
39
39
|
<prop name="inline" kind="react">
|
|
40
40
|
<description>Displays the stack as `inline-flex` instead of `flex`.</description>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<description>Aligns content on the vertical axis from the consumer point of view.</description>
|
|
59
59
|
</prop>
|
|
60
60
|
<prop name="vPadding" kind="react">
|
|
61
|
-
<description>Sets block padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
61
|
+
<description>Sets block padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset (e.g. bottom only `vPadding={[0, 'l']}`) — never `style`/`paddingBlock*` CSS when this prop covers it.</description>
|
|
62
62
|
</prop>
|
|
63
63
|
<prop name="wrap" kind="react">
|
|
64
64
|
<description>Allows direct children to wrap onto additional lines when space is constrained.</description>
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
<do>Semantic/native roots via `render` (`header`, `main`, `nav`, `section`, …) while keeping Stack as the layout engine</do>
|
|
10
10
|
<do>Opt into `@container stack` with `enableContainer` when layout should follow this Stack's inline-size, not the viewport</do>
|
|
11
11
|
<dont>Reaching for a plain wrapper `div` when Stack (optionally with `render` for a semantic tag) can own the box</dont>
|
|
12
|
+
<dont>Passing `render={<div />}` (or any host that matches Stack's default root) — omit `render`; use it only for a different host (`header`, `Surface`, …)</dont>
|
|
12
13
|
<dont>Assuming Stack expands direct children by default — pass `expandChildren` when children should grow on the flex main axis</dont>
|
|
13
14
|
<dont>Using `expandChildren` to fill parent width under `hAlign="center"` — that is `fullWidth` on the child Stack (self `inline-size`), not child flex-grow</dont>
|
|
14
15
|
<dont>Inventing `width: 100%` / `inline-size: 100%` / `w-full` / `.fullWidth` / size-fill `className` on content-sized children when this Stack can own the fill — or when children are intentionally fixed-size (e.g. IconButton)</dont>
|
|
15
|
-
<dont>Inventing CSS `gap` / `padding`
|
|
16
|
+
<dont>Inventing CSS `gap` / `padding` / `paddingBlock*` / `paddingInline*` / side paddings (`style` or className) when public spacing props cover it — one-sided inset uses `hPadding`/`vPadding` `[start, end]` tuples (e.g. `vPadding={[0, 'l']}`), not `paddingBlockEnd: '1rem'`</dont>
|
|
16
17
|
<dont>Putting `data-grow="false"` on an ancestor to control nested `expandChildren` — set it on the child that should not grow</dont>
|
|
17
18
|
<dont>Combining `fullWidth` with `inline` — prefer block flex when the root must fill parent inline size</dont>
|
|
18
19
|
<dont>Using viewport media queries when `@container stack` against this Stack's inline-size matches the design intent</dont>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
<description>Sets the hover background on hover-capable pointers using a base ramp layer (`1`–`3`), a literal CSS value, or the named token `primary` (`data-surface-hover-color="primary"`; CSS owns light/dark opacity via `--global-primary`).</description>
|
|
34
34
|
</prop>
|
|
35
35
|
<prop name="hPadding" kind="react">
|
|
36
|
-
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
36
|
+
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
|
|
37
37
|
</prop>
|
|
38
38
|
<prop name="overflow" kind="react">
|
|
39
39
|
<description>Sets root overflow through the `--vui-surface-overflow` custom property.</description>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
<description>Replaces the default `div` root using Base UI render semantics.</description>
|
|
49
49
|
</prop>
|
|
50
50
|
<prop name="vPadding" kind="react">
|
|
51
|
-
<description>Sets block padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
51
|
+
<description>Sets block padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset (e.g. bottom only `vPadding={[0, 'l']}`) — never `style`/`paddingBlock*` CSS when this prop covers it.</description>
|
|
52
52
|
</prop>
|
|
53
53
|
<prop name="background" kind="css">
|
|
54
54
|
<description>Resting surface background on the root.</description>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<do>Inset content with `hPadding` / `vPadding` — nest Stack/Grid for child gaps</do>
|
|
10
10
|
<do>Set `radius` to a token (or four-value tuple) on the outermost Surface that needs rounded corners; `radius="auto"` on nested Surfaces for concentric corners</do>
|
|
11
11
|
<dont>Passing `elevation={…}` on Surface — wrap with Elevator / `getElevationProps`</dont>
|
|
12
|
-
<dont>Inventing CSS `padding` / `gap`
|
|
12
|
+
<dont>Inventing CSS `padding` / `paddingBlock*` / `paddingInline*` / `gap` (`style` or className) when public padding props (and nested Stack) cover it — one-sided inset uses `hPadding`/`vPadding` `[start, end]` tuples (e.g. `vPadding={[0, 'l']}`), not `paddingBlockEnd`</dont>
|
|
13
13
|
<dont>Inventing `width: 100%` on Surface when a parent Stack `fullWidth` can own the fill</dont>
|
|
14
14
|
<dont>Overwriting transition shorthand without composing box-shadow when `data-elevation` is present</dont>
|
|
15
15
|
<dont>Assuming `hoverColor` applies on touch devices</dont>
|
|
@@ -61,6 +61,15 @@
|
|
|
61
61
|
<notes>From `auth/login-form` acknowledged state. `render={<p />}` for block semantics; default body size/tone.</notes>
|
|
62
62
|
</example>
|
|
63
63
|
|
|
64
|
+
<example title="Prose link">
|
|
65
|
+
<tsx><![CDATA[
|
|
66
|
+
<Text render={<a href="#" />} size="xs">
|
|
67
|
+
Forgot password?
|
|
68
|
+
</Text>
|
|
69
|
+
]]></tsx>
|
|
70
|
+
<notes>Text-as-a keeps preflight link chrome (animated underline). Do not set `data-no-link-style` — that attr is for painted chrome links only.</notes>
|
|
71
|
+
</example>
|
|
72
|
+
|
|
64
73
|
<example title="Empty-state centered">
|
|
65
74
|
<tsx><![CDATA[
|
|
66
75
|
<Stack hAlign="center" rowGap="m">
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
<description>Overrides font width as a percentage (100 = normal); maps to `font-stretch` and `wdth`.</description>
|
|
31
31
|
</prop>
|
|
32
32
|
<prop name="hPadding" kind="react">
|
|
33
|
-
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
33
|
+
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
|
|
34
34
|
</prop>
|
|
35
35
|
<prop name="lineHeight" kind="react">
|
|
36
|
-
<description>Controls line height through foundation leading tokens.</description>
|
|
36
|
+
<description>Controls line height through foundation leading tokens. Root Text without lineHeight uses leading `m`; omit on nested Text so line-height inherits the outer Text.</description>
|
|
37
37
|
</prop>
|
|
38
38
|
<prop name="maxWidth" kind="react">
|
|
39
39
|
<description>Constrains the maximum inline size of the rendered text block.</description>
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<description>Enables text selection on the rendered copy.</description>
|
|
52
52
|
</prop>
|
|
53
53
|
<prop name="size" kind="react">
|
|
54
|
-
<description>Selects a semantic body typography size from the active theme scale (`4xl` \| `3xl` \| `2xl` \| `xl` \| `l` \| `m` \| `s` \| `xs`).
|
|
54
|
+
<description>Selects a semantic body typography size from the active theme scale (`4xl` \| `3xl` \| `2xl` \| `xl` \| `l` \| `m` \| `s` \| `xs`). Root Text without size locks at body `m`; omit size on nested Text so font-size inherits the outer Text. Fluid clamp (prev step→token) applies when `fluid` is true and --effect-fluid-typography is empty. Theme effect initial still locks all Text. size xs is always fixed (min=max).</description>
|
|
55
55
|
</prop>
|
|
56
56
|
<prop name="tone" kind="react">
|
|
57
57
|
<description>Sets the semantic text color role when no literal `color` override is used. `default` inherits; `primary` → `--global-primary`; `muted` → `--global-muted`; `interactive` → `--global-interactive-text`; `positive` / `informative` / `danger` / `warning` → `--highlight-green` / `--highlight-blue` / `--highlight-red` / `--highlight-yellow`.</description>
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<description>Selects the `text-box` trim basis when `trim` is active.</description>
|
|
70
70
|
</prop>
|
|
71
71
|
<prop name="vPadding" kind="react">
|
|
72
|
-
<description>Sets block padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
72
|
+
<description>Sets block padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingBlock*` CSS when this prop covers it.</description>
|
|
73
73
|
</prop>
|
|
74
74
|
<prop name="weight" kind="react">
|
|
75
75
|
<description>Selects a semantic weight keyword or a numeric weight (`font-weight` + `wght` axis).</description>
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
<description>Optical size axis on the root (variable fonts).</description>
|
|
88
88
|
</prop>
|
|
89
89
|
<prop name="font-size" kind="css">
|
|
90
|
-
<description>Body font size; size prop presets via data attributes.
|
|
90
|
+
<description>Body font size; size prop presets via data attributes. Root without size uses body `m`; nested Text without `data-text-size` inherits the outer Text font-size. Clamp (prev step→token) applies only when --effect-fluid-typography is on and `fluid` is true (Text defaults fluid false).</description>
|
|
91
91
|
</prop>
|
|
92
92
|
<prop name="font-weight" kind="css">
|
|
93
93
|
<description>Font weight on the root; keyword presets or numeric via weight prop.</description>
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
<description>Font width percentage on the root (font-stretch + wdth).</description>
|
|
97
97
|
</prop>
|
|
98
98
|
<prop name="line-height" kind="css">
|
|
99
|
-
<description>Line height; lineHeight prop presets via data attributes.</description>
|
|
99
|
+
<description>Line height; lineHeight prop presets via data attributes. Nested Text without `data-text-line-height` inherits the outer Text line-height.</description>
|
|
100
100
|
</prop>
|
|
101
101
|
<prop name="letter-spacing" kind="css">
|
|
102
102
|
<description>Letter spacing on the root. Prefer the `tracking` React prop; set this hook only when theming beyond the prop.</description>
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
<do>Hero / status motion: nest Typewriter inside Text when body tone/size should drive the typed line</do>
|
|
9
9
|
<do>Ellipsis: ClampText `render={<Text … />}` — FitText when copy must scale to fill width instead of truncate</do>
|
|
10
10
|
<do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
|
|
11
|
+
<do>Inline prose link: `Text render={<a href=… />}` — keeps preflight link chrome (no `data-no-link-style`)</do>
|
|
11
12
|
<dont>Using Text for headings — use Title (`render` for h1–h6)</dont>
|
|
12
|
-
<dont>
|
|
13
|
+
<dont>Putting `data-no-link-style` on Text-as-a when the default underline hover is wanted</dont>
|
|
14
|
+
<dont>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` / `paddingBlock*` / `paddingInline*` when `tracking` / `transform` / Stack `fullWidth` / `hPadding`/`vPadding` tuples cover it</dont>
|
|
13
15
|
<dont>Setting body color with raw `--global-*` when `tone` covers the role</dont>
|
|
14
16
|
</usage>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<description>Overrides font width as a percentage (100 = normal); maps to `font-stretch` and `wdth`.</description>
|
|
29
29
|
</prop>
|
|
30
30
|
<prop name="hPadding" kind="react">
|
|
31
|
-
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
31
|
+
<description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
|
|
32
32
|
</prop>
|
|
33
33
|
<prop name="lineHeight" kind="react">
|
|
34
34
|
<description>Controls line height through foundation leading tokens.</description>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
<description>Selects the `text-box` trim basis when `trim` is active.</description>
|
|
65
65
|
</prop>
|
|
66
66
|
<prop name="vPadding" kind="react">
|
|
67
|
-
<description>Sets block padding using one token for both sides or a `[start, end]` tuple.</description>
|
|
67
|
+
<description>Sets block padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingBlock*` CSS when this prop covers it.</description>
|
|
68
68
|
</prop>
|
|
69
69
|
<prop name="weight" kind="react">
|
|
70
70
|
<description>Overrides the size preset weight (`font-weight` + `wght` axis).</description>
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
<do>Truncation: ClampText `render={<Title size=… render={<h* />} />}` — FitText wraps Title when short headlines must fill a constrained width</do>
|
|
8
8
|
<do>Fluid clamp across presets (`fluid` / theme `--effect-fluid-typography`); pass `fluid={false}` inside chrome or dense UI</do>
|
|
9
9
|
<do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
|
|
10
|
+
<do>Title-as-prose-link (`render={<a />}`) keeps preflight link chrome — omit `data-no-link-style`</do>
|
|
10
11
|
<dont>Assuming `size` changes the HTML tag — choose `render` explicitly</dont>
|
|
11
12
|
<dont>Using Title for long body paragraphs — use Text</dont>
|
|
12
|
-
<dont>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` when props / Stack cover it</dont>
|
|
13
|
+
<dont>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` / `paddingBlock*` / `paddingInline*` when props / Stack / `hPadding`/`vPadding` tuples cover it</dont>
|
|
13
14
|
</usage>
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
</attr>
|
|
20
20
|
<attr name="data-no-link-style">
|
|
21
21
|
<presence>boolean attribute on anchor elements</presence>
|
|
22
|
-
<effect>Excludes the anchor from preflight default link styling (interactive color,
|
|
22
|
+
<effect>Excludes the anchor from all preflight default link styling (interactive color, text-decoration reset, animated underline on hover/focus). Every a:any-link without this attribute gets that chrome — class presence alone does not opt out.</effect>
|
|
23
23
|
<values>
|
|
24
24
|
<value>(boolean presence)</value>
|
|
25
25
|
</values>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
<when>Component-owned or custom link chrome (ButtonLink, Chip-as-link, Menu.LinkItem, Surface-as-link, app anchors with their own paint)</when>
|
|
27
|
+
<when>Any anchor whose background must not receive the preflight underline gradient</when>
|
|
28
|
+
<when_not>Standard inline prose links (bare a, Text/Title render as a) that should keep preflight link styling</when_not>
|
|
29
|
+
<gotcha>A class on the anchor no longer suppresses link styles — only data-no-link-style does. Chrome components set it; prose Text/Title-as-a keep the underline with no extra markup.</gotcha>
|
|
30
30
|
</attr>
|
|
31
31
|
<attr name="data-elevation">
|
|
32
32
|
<presence>value required</presence>
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
<module id="typography">
|
|
52
52
|
<purpose>Semantic HTML element typography defaults and link chrome opt-out.</purpose>
|
|
53
53
|
<provides>element type defaults; data-no-link-style</provides>
|
|
54
|
-
<use>
|
|
55
|
-
<do_not>
|
|
54
|
+
<use>Every a:any-link gets preflight link chrome (interactive color + animated underline) unless data-no-link-style is set. Prose links — including Text/Title render as a — keep it. Chrome components set the attr (attributes.xml).</use>
|
|
55
|
+
<do_not>Rely on class presence to suppress link styles — use data-no-link-style only.</do_not>
|
|
56
56
|
</module>
|
|
57
57
|
<module id="vibrancy">
|
|
58
58
|
<purpose>Theme-gated backdrop-filter glass on opt-in hosts.</purpose>
|
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.6.
|
|
2
|
+
<index package="@viraui/react" version="2026.6.5" generated="2026-10-05T13:29:05.981Z">
|
|
3
3
|
<categories>
|
|
4
4
|
<category id="layout" />
|
|
5
5
|
<category id="action" />
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<ref id="bleed" units="meta,usage" />
|
|
11
11
|
</uses>
|
|
12
12
|
<do>Prefer Vira layout roots (`Stack`, `Grid`, `Masonry`, `Bleed`, `Surface`) and their public props for every consumer layout. Treat `Stack` as the default box: an enriched `div` (flex + gap/padding/align/`fullWidth`/`render`/…) — prefer it over bare wrapper `div`s even for simple nesting. Compose **with** these parts; default custom CSS = **app chrome** the system does not own. Prefer another Vira component (e.g. `Surface` for border/fill/radius) or `render` merge over a CSS class for chrome the library already owns. Do not open a CSS module to reshape a Vira control unless the consumer **expressly asked** for that override (still read `props`/`usage` first; a future override layer will formalize hooks).</do>
|
|
13
|
-
<dont>Do not invent bare wrapper `div`s (or `display:flex` / `display:grid` / CSS `gap` / `padding` / `width` / `inline-size` / border/radius chrome on raw tags or Vira control roots) when `Stack` or another layout primitive / `Surface` / a public `fullWidth`
|
|
13
|
+
<dont>Do not invent bare wrapper `div`s (or `display:flex` / `display:grid` / CSS `gap` / `padding` / `paddingBlock*` / `paddingInline*` / `width` / `inline-size` / border/radius chrome on raw tags or Vira control roots) when `Stack` or another layout primitive / `Surface` / a public `fullWidth` or `hPadding`/`vPadding` tuple covers it. Do not skip composition to reach for a one-off stylesheet — including size-fill classNames that would do nothing on fixed-size controls. Do not pass `render` that restates a component's default root (e.g. Stack `render={<div />}`) — omit `render` unless the host changes.</dont>
|
|
14
14
|
<tsx><![CDATA[
|
|
15
15
|
{/* Flex row + panel chrome: one DOM node via Stack render={<Surface />} */}
|
|
16
16
|
<Stack
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<ref id="button" units="meta" />
|
|
7
7
|
</uses>
|
|
8
8
|
<do>Own page/section flex with Stack. Pick semantic tags (`header`, `main`, `nav`, `section`, …) via `render={<tag />}` — keep Stack as the layout engine. CSS modules may set min-block-size / scroll; never `display:flex`.</do>
|
|
9
|
-
<dont>Do not use raw `header`/`main`/`nav` + `display:flex` / CSS gap for jobs Stack owns. Tag choice ≠ layout engine.</dont>
|
|
9
|
+
<dont>Do not use raw `header`/`main`/`nav` + `display:flex` / CSS gap for jobs Stack owns. Tag choice ≠ layout engine. Do not pass `render={<div />}` — Stack already defaults to `div`; omit `render` unless the host changes.</dont>
|
|
10
10
|
<tsx><![CDATA[
|
|
11
11
|
<Stack rowGap="m" expandChildren>
|
|
12
12
|
<Stack
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viraui/react",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.5",
|
|
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.6.
|
|
31
|
+
"@viraui/foundation": "2026.6.5",
|
|
32
32
|
"@viraui/icons": "2026.1.0",
|
|
33
33
|
"@viraui/postcss-config": "2026.0.0",
|
|
34
34
|
"@viraui/tsconfig": "2026.0.0",
|