@tanishqkancharla/maui 0.0.38 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Button.js +14 -9
- package/dist/components/Checkbox.js +2 -2
- package/dist/components/Code.d.ts +5 -1
- package/dist/components/Code.js +5 -2
- package/dist/components/Input.d.ts +11 -4
- package/dist/components/Input.js +59 -16
- package/dist/components/Radio.js +2 -2
- package/dist/components/Select.js +2 -2
- package/dist/components/Slider.js +2 -2
- package/dist/components/Switch.js +2 -2
- package/dist/components/Table.js +2 -2
- package/dist/maui.d.ts +2 -2
- package/dist/tokens/shadow.d.ts +2 -0
- package/dist/tokens/shadow.js +2 -0
- package/package.json +1 -1
- package/skills/maui/references/components/buttons.md +2 -3
- package/skills/maui/references/components/form-controls.md +6 -4
- package/skills/maui/references/design_conventions.md +5 -3
- package/skills/maui/references/tokens.md +6 -4
- package/src/components/Button.tsx +42 -28
- package/src/components/Checkbox.tsx +2 -2
- package/src/components/Code.tsx +19 -2
- package/src/components/Input.tsx +129 -23
- package/src/components/Radio.tsx +2 -2
- package/src/components/Select.tsx +3 -3
- package/src/components/Slider.tsx +2 -2
- package/src/components/Switch.tsx +2 -2
- package/src/components/Table.tsx +2 -2
- package/src/maui.ts +3 -1
- package/src/pages/AboutPage.tsx +6 -4
- package/src/pages/AssistantMessagePage.tsx +4 -3
- package/src/pages/AvatarPage.tsx +3 -2
- package/src/pages/BackgroundColorTokenPage.tsx +13 -13
- package/src/pages/BordersTokenPage.tsx +14 -13
- package/src/pages/ButtonsPage.tsx +11 -10
- package/src/pages/ColorTokenPage.tsx +18 -14
- package/src/pages/CornerRadiusTokenPage.tsx +19 -18
- package/src/pages/CrossfadePage.tsx +6 -5
- package/src/pages/DrawerPage.tsx +9 -8
- package/src/pages/EditorPage.tsx +8 -7
- package/src/pages/FlexTokenPage.tsx +52 -52
- package/src/pages/FocusRingTokenPage.tsx +4 -3
- package/src/pages/FuzzyStringPage.tsx +3 -3
- package/src/pages/IconsPage.tsx +13 -12
- package/src/pages/LayoutTokenPage.tsx +9 -9
- package/src/pages/LayoutUtilitiesPage.tsx +13 -12
- package/src/pages/LoadingScreenPage.tsx +4 -3
- package/src/pages/Maui.tsx +82 -2
- package/src/pages/MenuPage.tsx +2 -1
- package/src/pages/MotionTokenPage.tsx +3 -3
- package/src/pages/ProsePage.tsx +4 -3
- package/src/pages/ShadowTokenPage.tsx +20 -10
- package/src/pages/SidebarPage.tsx +2 -1
- package/src/pages/SizingTokenPage.tsx +6 -5
- package/src/pages/SpacingTokenPage.tsx +3 -2
- package/src/pages/TextTokenPage.tsx +24 -23
- package/src/tokens/shadow.ts +2 -0
|
@@ -10,17 +10,18 @@ import { colors, } from "../tokens/colors";
|
|
|
10
10
|
import { focusRing } from "../tokens/focusRing";
|
|
11
11
|
import { radius } from "../tokens/radius";
|
|
12
12
|
import { motion } from "../tokens/motion";
|
|
13
|
-
import {
|
|
13
|
+
import { shadowVars } from "../tokens/shadow";
|
|
14
14
|
import { controlSize } from "../tokens/sizing";
|
|
15
15
|
import { spacing } from "../tokens/spacing";
|
|
16
16
|
import { text } from "../tokens/text";
|
|
17
17
|
import { memoize } from "../utils/memoize";
|
|
18
|
+
const buttonShadow = shadowVars.control;
|
|
18
19
|
const disabledRaised = {
|
|
19
20
|
cursor: "default",
|
|
20
21
|
opacity: 1,
|
|
21
22
|
color: colors.gray[8],
|
|
22
23
|
backgroundColor: backgroundColor.element,
|
|
23
|
-
boxShadow:
|
|
24
|
+
boxShadow: buttonShadow,
|
|
24
25
|
};
|
|
25
26
|
const disabledQuiet = {
|
|
26
27
|
cursor: "default",
|
|
@@ -29,7 +30,7 @@ const disabledQuiet = {
|
|
|
29
30
|
backgroundColor: "transparent",
|
|
30
31
|
boxShadow: "none",
|
|
31
32
|
};
|
|
32
|
-
const buttonBaseClass = style(text({ size: "xs", fontWeight: 400, color: "highContrast" }), focusRing("&:focus-visible",
|
|
33
|
+
const buttonBaseClass = style(text({ size: "xs", fontWeight: 400, color: "highContrast" }), focusRing("&:focus-visible", buttonShadow), motion.standard("box-shadow", "background-color", "color"), spacing.padding({ x: 6, y: 4 }), radius.sm, {
|
|
33
34
|
display: "inline-flex",
|
|
34
35
|
alignItems: "center",
|
|
35
36
|
justifyContent: "center",
|
|
@@ -38,6 +39,7 @@ const buttonBaseClass = style(text({ size: "xs", fontWeight: 400, color: "highCo
|
|
|
38
39
|
height: controlSize.height,
|
|
39
40
|
minWidth: controlSize.minTarget,
|
|
40
41
|
border: "none",
|
|
42
|
+
boxShadow: buttonShadow,
|
|
41
43
|
textOverflow: "ellipsis",
|
|
42
44
|
overflow: "hidden",
|
|
43
45
|
whiteSpace: "nowrap",
|
|
@@ -129,16 +131,19 @@ function darkerFill(color) {
|
|
|
129
131
|
function onSolidText(color) {
|
|
130
132
|
return `oklch(from ${color} clamp(0.2, (0.75 - l) * 100, 0.99) 0 0)`;
|
|
131
133
|
}
|
|
134
|
+
function primaryButtonShadow(color) {
|
|
135
|
+
return `inset 0 1px #ffffff18, inset 0 0 1px .5px #fff2, 0 1px .5px #0003, 0 0 3px -1px color-mix(in oklch, ${color}, black 35%)`;
|
|
136
|
+
}
|
|
132
137
|
const coloredButtonClass = memoize((variant, color) => {
|
|
133
138
|
if (isCssColor(color)) {
|
|
134
139
|
const fill = opaqueColor(color);
|
|
135
140
|
if (variant === "primary") {
|
|
136
|
-
const edge = tintedSubtle(fill);
|
|
137
141
|
const hover = darkerFill(fill);
|
|
138
|
-
|
|
142
|
+
const primaryShadow = primaryButtonShadow(fill);
|
|
143
|
+
return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
|
|
139
144
|
color: onSolidText(fill),
|
|
140
145
|
backgroundColor: fill,
|
|
141
|
-
boxShadow:
|
|
146
|
+
boxShadow: primaryShadow,
|
|
142
147
|
"&:hover:not(:disabled)": {
|
|
143
148
|
backgroundColor: hover,
|
|
144
149
|
},
|
|
@@ -152,11 +157,11 @@ const coloredButtonClass = memoize((variant, color) => {
|
|
|
152
157
|
}
|
|
153
158
|
const scale = paletteFill(color);
|
|
154
159
|
if (variant === "primary") {
|
|
155
|
-
const
|
|
156
|
-
return style(buttonBaseClass, focusRing("&:focus-visible",
|
|
160
|
+
const primaryShadow = primaryButtonShadow(scale[9]);
|
|
161
|
+
return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
|
|
157
162
|
color: darkTextOnSolid.has(color) ? scale[12] : "white",
|
|
158
163
|
backgroundColor: scale[9],
|
|
159
|
-
boxShadow:
|
|
164
|
+
boxShadow: primaryShadow,
|
|
160
165
|
"&:hover:not(:disabled)": {
|
|
161
166
|
backgroundColor: scale[10],
|
|
162
167
|
},
|
|
@@ -18,7 +18,7 @@ const checkboxSize = defineVars({
|
|
|
18
18
|
box: { default: "14px", [LARGE_SCALE]: "20px" },
|
|
19
19
|
checkmark: { default: "10px", [LARGE_SCALE]: "14px" },
|
|
20
20
|
});
|
|
21
|
-
const checkboxClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .checkbox-input:focus-visible + .checkbox-toggle", shadowVars.
|
|
21
|
+
const checkboxClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .checkbox-input:focus-visible + .checkbox-toggle", shadowVars.control), {
|
|
22
22
|
position: "relative",
|
|
23
23
|
width: "fit-content",
|
|
24
24
|
minHeight: controlSize.minTarget,
|
|
@@ -34,7 +34,7 @@ const checkboxClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("&
|
|
|
34
34
|
transform: "scale(1)",
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
|
-
const checkboxToggleClass = style(flex({ alignItems: "center", justifyContent: "center" }), radius["2xs"], motion.standard("background-color"), shadow.
|
|
37
|
+
const checkboxToggleClass = style(flex({ alignItems: "center", justifyContent: "center" }), radius["2xs"], motion.standard("background-color"), shadow.control, {
|
|
38
38
|
pointerEvents: "none",
|
|
39
39
|
flexShrink: 0,
|
|
40
40
|
width: checkboxSize.box,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
type CodeProps = React.HTMLAttributes<HTMLElement>;
|
|
3
|
+
export type KbdVariant = "default" | "quiet";
|
|
4
|
+
export type KbdProps = CodeProps & {
|
|
5
|
+
variant?: KbdVariant;
|
|
6
|
+
};
|
|
3
7
|
export declare function Code({ className, children, ...props }: CodeProps): React.JSX.Element;
|
|
4
|
-
export declare function Kbd({ className, children, ...props }:
|
|
8
|
+
export declare function Kbd({ className, children, variant, ...props }: KbdProps): React.JSX.Element;
|
|
5
9
|
export {};
|
package/dist/components/Code.js
CHANGED
|
@@ -22,12 +22,15 @@ const kbdClass = style(text({ size: "xs", fontWeight: 400, color: "lowContrast"
|
|
|
22
22
|
paddingBlock: 0,
|
|
23
23
|
whiteSpace: "nowrap",
|
|
24
24
|
});
|
|
25
|
+
const quietKbdClass = style(kbdClass, {
|
|
26
|
+
backgroundColor: "transparent",
|
|
27
|
+
});
|
|
25
28
|
export function Code({ className, children, ...props }) {
|
|
26
29
|
const codeClassName = useStyles(codeClass);
|
|
27
30
|
return (_jsx("code", { ...props, className: joinClassNames(codeClassName, className), children: children }));
|
|
28
31
|
}
|
|
29
|
-
export function Kbd({ className, children, ...props }) {
|
|
30
|
-
const kbdClassName = useStyles(kbdClass);
|
|
32
|
+
export function Kbd({ className, children, variant = "default", ...props }) {
|
|
33
|
+
const kbdClassName = useStyles(variant === "quiet" ? quietKbdClass : kbdClass);
|
|
31
34
|
return (_jsx("kbd", { ...props, className: joinClassNames(kbdClassName, className), children: children }));
|
|
32
35
|
}
|
|
33
36
|
function joinClassNames(...classNames) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { AriaNumberFieldProps, AriaSearchFieldProps, AriaTextFieldOptions } from "react-aria";
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
type KeyboardHintProps = {
|
|
3
|
+
/** A single-character shortcut displayed at the inline end while unfocused. */
|
|
4
|
+
keyboardHint?: string;
|
|
5
|
+
};
|
|
6
|
+
export type TextFieldProps = AriaTextFieldOptions<"input"> & KeyboardHintProps;
|
|
7
|
+
export type SearchFieldProps = AriaSearchFieldProps & KeyboardHintProps & {
|
|
8
|
+
"aria-keyshortcuts"?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function TextField(props: TextFieldProps): import("react").JSX.Element;
|
|
11
|
+
export declare function SearchField(props: SearchFieldProps): import("react").JSX.Element;
|
|
5
12
|
export declare function NumberField(props: AriaNumberFieldProps): import("react").JSX.Element;
|
|
6
|
-
export declare function QuietTextField(props:
|
|
13
|
+
export declare function QuietTextField(props: TextFieldProps): import("react").JSX.Element;
|
|
7
14
|
export {};
|
package/dist/components/Input.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRef } from "react";
|
|
3
|
-
import { useButton, useLocale, useNumberField, useSearchField, useTextField, } from "react-aria";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
|
+
import { mergeProps, useButton, useLocale, useNumberField, useSearchField, useTextField, } from "react-aria";
|
|
4
4
|
import { useNumberFieldState, useSearchFieldState } from "react-stately";
|
|
5
5
|
import { defineVars, style, useStyles } from "purse-styles";
|
|
6
6
|
import { LARGE_SCALE } from "../theme/dataScale";
|
|
@@ -12,11 +12,12 @@ import { radius } from "../tokens/radius";
|
|
|
12
12
|
import { shadow, shadowVars } from "../tokens/shadow";
|
|
13
13
|
import { controlSize, iconSizeValues } from "../tokens/sizing";
|
|
14
14
|
import { spacing } from "../tokens/spacing";
|
|
15
|
-
import { text } from "../tokens/text";
|
|
15
|
+
import { monospace, text } from "../tokens/text";
|
|
16
|
+
import { Kbd } from "./Code";
|
|
16
17
|
import { Icons } from "./Icons";
|
|
17
18
|
const inputText = text({ size: "sm", fontWeight: 400, color: "highContrast" });
|
|
18
19
|
const numberFieldDivider = `color-mix(in oklch, ${colors.gray[12]} 5%, ${backgroundColor.element})`;
|
|
19
|
-
const inputClass = style(inputText, focusRing("&:focus-visible", shadowVars.
|
|
20
|
+
const inputClass = style(inputText, focusRing("&:focus-visible", shadowVars.control), motion.standard("background", "border-color"), radius.sm, spacing.padding({ x: 4, y: 2 }), shadow.control, {
|
|
20
21
|
width: "100%",
|
|
21
22
|
minWidth: 0,
|
|
22
23
|
height: controlSize.height,
|
|
@@ -31,18 +32,52 @@ const inputClass = style(inputText, focusRing("&:focus-visible", shadowVars.subt
|
|
|
31
32
|
background: colors.gray[2],
|
|
32
33
|
},
|
|
33
34
|
"&[aria-invalid='true']:not(:focus-visible)": {
|
|
34
|
-
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.
|
|
35
|
+
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.control}`,
|
|
35
36
|
},
|
|
36
37
|
"&::placeholder": {
|
|
37
38
|
fontStyle: "italic",
|
|
38
39
|
color: colors.gray[8],
|
|
39
40
|
},
|
|
40
41
|
});
|
|
42
|
+
const inputFrameClass = style({
|
|
43
|
+
position: "relative",
|
|
44
|
+
width: "100%",
|
|
45
|
+
});
|
|
46
|
+
const inputWithHintClass = style({
|
|
47
|
+
paddingRight: "28px",
|
|
48
|
+
});
|
|
49
|
+
const keyboardHintClass = style(monospace, {
|
|
50
|
+
position: "absolute",
|
|
51
|
+
top: "50%",
|
|
52
|
+
right: spacing.value(4),
|
|
53
|
+
transform: "translateY(-50%)",
|
|
54
|
+
pointerEvents: "none",
|
|
55
|
+
});
|
|
56
|
+
function useKeyboardHint() {
|
|
57
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
58
|
+
return {
|
|
59
|
+
isFocused,
|
|
60
|
+
focusProps: {
|
|
61
|
+
onFocus: () => setIsFocused(true),
|
|
62
|
+
onBlur: () => setIsFocused(false),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function KeyboardHint(props) {
|
|
67
|
+
const className = useStyles(keyboardHintClass);
|
|
68
|
+
return (_jsx(Kbd, { variant: "quiet", className: className, "aria-hidden": "true", children: props.children }));
|
|
69
|
+
}
|
|
41
70
|
export function TextField(props) {
|
|
71
|
+
const { keyboardHint, ...fieldProps } = props;
|
|
42
72
|
const ref = useRef(null);
|
|
43
|
-
const { inputProps } = useTextField(
|
|
44
|
-
const
|
|
45
|
-
|
|
73
|
+
const { inputProps } = useTextField(fieldProps, ref);
|
|
74
|
+
const { isFocused, focusProps } = useKeyboardHint();
|
|
75
|
+
const frameClassName = useStyles(inputFrameClass);
|
|
76
|
+
const className = useStyles(inputClass, keyboardHint ? inputWithHintClass : undefined);
|
|
77
|
+
const input = (_jsx("input", { className: className, ref: ref, ...mergeProps(inputProps, focusProps) }));
|
|
78
|
+
if (!keyboardHint)
|
|
79
|
+
return input;
|
|
80
|
+
return (_jsxs("div", { className: frameClassName, children: [input, !isFocused ? _jsx(KeyboardHint, { children: keyboardHint }) : null] }));
|
|
46
81
|
}
|
|
47
82
|
const searchFieldSize = defineVars({
|
|
48
83
|
clearSize: { default: "16px", [LARGE_SCALE]: "40px" },
|
|
@@ -85,17 +120,19 @@ const searchFieldClass = style(focusRing("& button:focus-visible"), {
|
|
|
85
120
|
},
|
|
86
121
|
});
|
|
87
122
|
export function SearchField(props) {
|
|
123
|
+
const { keyboardHint, "aria-keyshortcuts": ariaKeyShortcuts, ...fieldProps } = props;
|
|
88
124
|
const ref = useRef(null);
|
|
89
|
-
const state = useSearchFieldState(
|
|
90
|
-
const { inputProps } = useSearchField(
|
|
125
|
+
const state = useSearchFieldState(fieldProps);
|
|
126
|
+
const { inputProps } = useSearchField(fieldProps, state, ref);
|
|
127
|
+
const { isFocused, focusProps } = useKeyboardHint();
|
|
91
128
|
const inputClassName = useStyles(inputClass);
|
|
92
129
|
const searchClassName = useStyles(searchFieldClass);
|
|
93
|
-
return (_jsxs("div", { className: searchClassName, children: [_jsx("input", { className: inputClassName, ref: ref, ...inputProps }), state.value !== "" && (_jsx("button", { "aria-label": "Clear search", type: "button", onMouseDown: (event) => event.preventDefault(), onClick: () => state.setValue(""), children: _jsx(Icons.CircleX, {}) }))] }));
|
|
130
|
+
return (_jsxs("div", { className: searchClassName, children: [_jsx("input", { className: inputClassName, ref: ref, ...mergeProps(inputProps, focusProps), "aria-keyshortcuts": ariaKeyShortcuts }), keyboardHint && !isFocused && state.value === "" ? (_jsx(KeyboardHint, { children: keyboardHint })) : null, state.value !== "" && (_jsx("button", { "aria-label": "Clear search", type: "button", onMouseDown: (event) => event.preventDefault(), onClick: () => state.setValue(""), children: _jsx(Icons.CircleX, {}) }))] }));
|
|
94
131
|
}
|
|
95
132
|
const numberFieldSize = defineVars({
|
|
96
133
|
stepperWidth: { default: "24px", [LARGE_SCALE]: "40px" },
|
|
97
134
|
});
|
|
98
|
-
const numberFieldClass = style(focusRing("&:has(:focus-visible)", shadowVars.
|
|
135
|
+
const numberFieldClass = style(focusRing("&:has(:focus-visible)", shadowVars.control), motion.standard("background", "border-color"), radius.sm, shadow.control, {
|
|
99
136
|
display: "flex",
|
|
100
137
|
alignItems: "center",
|
|
101
138
|
width: "100%",
|
|
@@ -108,7 +145,7 @@ const numberFieldClass = style(focusRing("&:has(:focus-visible)", shadowVars.sub
|
|
|
108
145
|
background: colors.gray[2],
|
|
109
146
|
},
|
|
110
147
|
"&:has(input[aria-invalid='true']):not(:has(:focus-visible))": {
|
|
111
|
-
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.
|
|
148
|
+
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.control}`,
|
|
112
149
|
},
|
|
113
150
|
"& .number-stepper": {
|
|
114
151
|
display: "flex",
|
|
@@ -195,8 +232,14 @@ const quietInputClass = style(inputText, focusRing(), motion.standard("backgroun
|
|
|
195
232
|
},
|
|
196
233
|
});
|
|
197
234
|
export function QuietTextField(props) {
|
|
235
|
+
const { keyboardHint, ...fieldProps } = props;
|
|
198
236
|
const ref = useRef(null);
|
|
199
|
-
const { inputProps } = useTextField(
|
|
200
|
-
const
|
|
201
|
-
|
|
237
|
+
const { inputProps } = useTextField(fieldProps, ref);
|
|
238
|
+
const { isFocused, focusProps } = useKeyboardHint();
|
|
239
|
+
const frameClassName = useStyles(inputFrameClass);
|
|
240
|
+
const className = useStyles(quietInputClass, keyboardHint ? inputWithHintClass : undefined);
|
|
241
|
+
const input = (_jsx("input", { className: className, ref: ref, ...mergeProps(inputProps, focusProps) }));
|
|
242
|
+
if (!keyboardHint)
|
|
243
|
+
return input;
|
|
244
|
+
return (_jsxs("div", { className: frameClassName, children: [input, !isFocused ? _jsx(KeyboardHint, { children: keyboardHint }) : null] }));
|
|
202
245
|
}
|
package/dist/components/Radio.js
CHANGED
|
@@ -21,7 +21,7 @@ export function RadioOptionGroup(props) {
|
|
|
21
21
|
const className = useStyles(radioGroupClass);
|
|
22
22
|
return (_jsx(RadioOptionGroupContext.Provider, { value: state, children: _jsxs("div", { className: className, ...groupProps, children: [_jsx(Label, { ...labelProps, children: label }), children] }) }));
|
|
23
23
|
}
|
|
24
|
-
const radioClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .radio-input:focus-visible + .radio-toggle", shadowVars.
|
|
24
|
+
const radioClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .radio-input:focus-visible + .radio-toggle", shadowVars.control), {
|
|
25
25
|
position: "relative",
|
|
26
26
|
width: "fit-content",
|
|
27
27
|
"&:hover .radio-toggle": {
|
|
@@ -34,7 +34,7 @@ const radioClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .r
|
|
|
34
34
|
transform: "scale(1)",
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
|
-
const radioToggleClass = style(radius.circle, motion.standard("background-color"), shadow.
|
|
37
|
+
const radioToggleClass = style(radius.circle, motion.standard("background-color"), shadow.control, {
|
|
38
38
|
pointerEvents: "none",
|
|
39
39
|
position: "relative",
|
|
40
40
|
width: "14px",
|
|
@@ -19,7 +19,7 @@ const selectClass = style({
|
|
|
19
19
|
gap: spacing.value(3),
|
|
20
20
|
width: "100%",
|
|
21
21
|
});
|
|
22
|
-
const triggerClass = style(text({ size: "sm", fontWeight: 400, color: "highContrast" }), focusRing("&:focus-visible", shadowVars.
|
|
22
|
+
const triggerClass = style(text({ size: "sm", fontWeight: 400, color: "highContrast" }), focusRing("&:focus-visible", shadowVars.control), motion.standard("background", "border-color"), radius.sm, spacing.padding({ x: 4, y: 2 }), shadow.control, {
|
|
23
23
|
display: "flex",
|
|
24
24
|
alignItems: "center",
|
|
25
25
|
height: controlSize.height,
|
|
@@ -39,7 +39,7 @@ const triggerClass = style(text({ size: "sm", fontWeight: 400, color: "highContr
|
|
|
39
39
|
background: colors.gray[2],
|
|
40
40
|
},
|
|
41
41
|
"&[data-invalid]:not(:focus-visible)": {
|
|
42
|
-
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.
|
|
42
|
+
boxShadow: `0 0 0 1px light-dark(#ce2c31, #e5484d), ${shadowVars.control}`,
|
|
43
43
|
},
|
|
44
44
|
});
|
|
45
45
|
const valueClass = style({
|
|
@@ -12,7 +12,7 @@ const sliderSize = defineVars({
|
|
|
12
12
|
thumb: { default: "12px", [LARGE_SCALE]: "20px" },
|
|
13
13
|
track: { default: "18px", [LARGE_SCALE]: "40px" },
|
|
14
14
|
});
|
|
15
|
-
const sliderClass = style(focusRing("& .slider-thumb:has(input:focus-visible)", shadowVars.
|
|
15
|
+
const sliderClass = style(focusRing("& .slider-thumb:has(input:focus-visible)", shadowVars.control), {
|
|
16
16
|
width: "240px",
|
|
17
17
|
"& .slider-label-row": {
|
|
18
18
|
display: "flex",
|
|
@@ -49,7 +49,7 @@ const sliderClass = style(focusRing("& .slider-thumb:has(input:focus-visible)",
|
|
|
49
49
|
height: sliderSize.thumb,
|
|
50
50
|
borderRadius: "100%",
|
|
51
51
|
background: colors.gray[12],
|
|
52
|
-
boxShadow: shadowVars.
|
|
52
|
+
boxShadow: shadowVars.control,
|
|
53
53
|
transform: "translateX(-50%)",
|
|
54
54
|
},
|
|
55
55
|
"& .slider-thumb[data-dragging='true']": {
|
|
@@ -30,7 +30,7 @@ const switchOff = defineVars({
|
|
|
30
30
|
[DARK_THEME]: colors.gray[2],
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
|
-
const switchClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .switch-input:focus-visible + .switch-toggle", shadowVars.
|
|
33
|
+
const switchClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .switch-input:focus-visible + .switch-toggle", shadowVars.control), {
|
|
34
34
|
position: "relative",
|
|
35
35
|
width: "fit-content",
|
|
36
36
|
minHeight: controlSize.minTarget,
|
|
@@ -44,7 +44,7 @@ const switchClass = style(flex({ alignItems: "center", gap: 3 }), focusRing("& .
|
|
|
44
44
|
transform: `translateX(calc(${switchSize.width} - ${switchSize.thumb} - 4px))`,
|
|
45
45
|
},
|
|
46
46
|
});
|
|
47
|
-
const switchToggleClass = style(radius.pill, motion.standard("background-color"), shadow.
|
|
47
|
+
const switchToggleClass = style(radius.pill, motion.standard("background-color"), shadow.control, {
|
|
48
48
|
position: "relative",
|
|
49
49
|
flexShrink: 0,
|
|
50
50
|
width: switchSize.width,
|
package/dist/components/Table.js
CHANGED
|
@@ -82,7 +82,7 @@ const tableCaptionClass = style(text({ size: "xs", fontWeight: 400, color: "lowC
|
|
|
82
82
|
const emptyStateClass = style(text({ size: "sm", fontWeight: 400, color: "lowContrast" }), spacing.padding({ x: 4, y: 8 }), {
|
|
83
83
|
textAlign: "center",
|
|
84
84
|
});
|
|
85
|
-
const tableSelectionCheckboxClass = style(flex({ alignItems: "center", justifyContent: "center" }), focusRing("&[data-focus-visible] .checkbox-toggle", shadowVars.
|
|
85
|
+
const tableSelectionCheckboxClass = style(flex({ alignItems: "center", justifyContent: "center" }), focusRing("&[data-focus-visible] .checkbox-toggle", shadowVars.control), {
|
|
86
86
|
position: "relative",
|
|
87
87
|
width: "fit-content",
|
|
88
88
|
cursor: "default",
|
|
@@ -108,7 +108,7 @@ const tableSelectionCheckboxClass = style(flex({ alignItems: "center", justifyCo
|
|
|
108
108
|
transform: "scale(1)",
|
|
109
109
|
},
|
|
110
110
|
});
|
|
111
|
-
const tableSelectionToggleClass = style(flex({ alignItems: "center", justifyContent: "center" }), radius["2xs"], shadow.
|
|
111
|
+
const tableSelectionToggleClass = style(flex({ alignItems: "center", justifyContent: "center" }), radius["2xs"], shadow.control, {
|
|
112
112
|
pointerEvents: "none",
|
|
113
113
|
width: "14px",
|
|
114
114
|
height: "14px",
|
package/dist/maui.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export { Checkbox } from "./components/Checkbox";
|
|
|
25
25
|
export { Switch } from "./components/Switch";
|
|
26
26
|
export { Slider } from "./components/Slider";
|
|
27
27
|
export { RadioOptionGroup, RadioOption } from "./components/Radio";
|
|
28
|
-
export { TextField, SearchField, NumberField, QuietTextField, } from "./components/Input";
|
|
28
|
+
export { TextField, SearchField, NumberField, QuietTextField, type TextFieldProps, type SearchFieldProps, } from "./components/Input";
|
|
29
29
|
export { Select, SelectItem, type SelectProps, type SelectItemProps, } from "./components/Select";
|
|
30
30
|
export { Dialog } from "./components/Dialog";
|
|
31
31
|
export { Tooltip } from "./components/Tooltip";
|
|
@@ -45,6 +45,6 @@ export { Flex, Gap, Spacer, Divider, type FlexShadow, type FlexRadius, type Flex
|
|
|
45
45
|
export { Text, type TextProps } from "./components/Text";
|
|
46
46
|
export { H1, H2, H3, H4, P, Label, labelText, Blockquote, Ul, Ol, Li, Link, } from "./components/Typography";
|
|
47
47
|
export { CodeBlock } from "./components/CodeBlock";
|
|
48
|
-
export { Code, Kbd } from "./components/Code";
|
|
48
|
+
export { Code, Kbd, type KbdProps, type KbdVariant } from "./components/Code";
|
|
49
49
|
export { CollectionPopover, type CollectionPopoverProps, } from "./components/CollectionPopover";
|
|
50
50
|
export { navigationItem } from "./components/navigationItem";
|
package/dist/tokens/shadow.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const shadowVars: import("purse-styles").VariableGroup<{
|
|
2
|
+
readonly control: "inset 0 1px #ffffff08, inset 0 0 1px .5px #fff1, 0 1px .5px #00000018, 0 0 3px -1px #000a";
|
|
2
3
|
readonly subtle: `rgba(var(--${string}), 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, var(--${string})) 0px 1px 1px -0.5px, rgba(0, 0, 0, var(--${string})) 0px 3px 3px -1.5px`;
|
|
3
4
|
readonly medium: `rgba(var(--${string}), 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, var(--${string})) 0px 1px 1px -0.5px, rgba(0, 0, 0, var(--${string})) 0px 3px 3px -1.5px, rgba(0, 0, 0, var(--${string})) 0px 6px 6px -3px`;
|
|
4
5
|
readonly strong: `rgba(var(--${string}), 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, var(--${string})) 0px 1px 1px -0.5px, rgba(0, 0, 0, var(--${string})) 0px 3px 3px -1.5px, rgba(0, 0, 0, var(--${string})) 0px 6px 6px -3px, rgba(0, 0, 0, calc(var(--${string}) * 0.67)) 0px 12px 12px -6px, rgba(0, 0, 0, calc(var(--${string}) * 0.67)) 0px 24px 24px -12px`;
|
|
@@ -6,6 +7,7 @@ export declare const shadowVars: import("purse-styles").VariableGroup<{
|
|
|
6
7
|
/** Same offsets as `shadowVars.subtle`, with the ring and blurs tinted from `color`. */
|
|
7
8
|
export declare function tintedSubtle(color: string): string;
|
|
8
9
|
export declare const shadow: {
|
|
10
|
+
control: import("purse-styles").StyleElement;
|
|
9
11
|
subtle: import("purse-styles").StyleElement;
|
|
10
12
|
medium: import("purse-styles").StyleElement;
|
|
11
13
|
strong: import("purse-styles").StyleElement;
|
package/dist/tokens/shadow.js
CHANGED
|
@@ -13,6 +13,7 @@ const shadowParams = defineVars({
|
|
|
13
13
|
const rgb = shadowParams.foregroundRgb;
|
|
14
14
|
const blur = shadowParams.blurOpacity;
|
|
15
15
|
export const shadowVars = defineVars({
|
|
16
|
+
control: `inset 0 1px #ffffff08, inset 0 0 1px .5px #fff1, 0 1px .5px #00000018, 0 0 3px -1px #000a`,
|
|
16
17
|
subtle: `rgba(${rgb}, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, ${blur}) 0px 1px 1px -0.5px, rgba(0, 0, 0, ${blur}) 0px 3px 3px -1.5px`,
|
|
17
18
|
medium: `rgba(${rgb}, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, ${blur}) 0px 1px 1px -0.5px, rgba(0, 0, 0, ${blur}) 0px 3px 3px -1.5px, rgba(0, 0, 0, ${blur}) 0px 6px 6px -3px`,
|
|
18
19
|
strong: `rgba(${rgb}, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, ${blur}) 0px 1px 1px -0.5px, rgba(0, 0, 0, ${blur}) 0px 3px 3px -1.5px, rgba(0, 0, 0, ${blur}) 0px 6px 6px -3px, rgba(0, 0, 0, calc(${blur} * 0.67)) 0px 12px 12px -6px, rgba(0, 0, 0, calc(${blur} * 0.67)) 0px 24px 24px -12px`,
|
|
@@ -22,6 +23,7 @@ export function tintedSubtle(color) {
|
|
|
22
23
|
return `oklch(from ${color} l c h / 0.08) 0px 0px 0px 1px, oklch(from ${color} l c h / ${blur}) 0px 1px 1px -0.5px, oklch(from ${color} l c h / ${blur}) 0px 3px 3px -1.5px`;
|
|
23
24
|
}
|
|
24
25
|
export const shadow = {
|
|
26
|
+
control: style({ boxShadow: shadowVars.control }),
|
|
25
27
|
subtle: style({ boxShadow: shadowVars.subtle }),
|
|
26
28
|
medium: style({ boxShadow: shadowVars.medium }),
|
|
27
29
|
strong: style({ boxShadow: shadowVars.strong }),
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ Gallery: `/components/buttons`. Import from `"maui"`. Overlay and Dialog live on
|
|
|
4
4
|
|
|
5
5
|
## `Button`
|
|
6
6
|
|
|
7
|
-
Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`, so it works as a `MenuTrigger`, `Select`, `DialogTrigger`, `ComboBox`, or `DatePicker` child. Height 28px, `shadow.
|
|
7
|
+
Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`, so it works as a `MenuTrigger`, `Select`, `DialogTrigger`, `ComboBox`, or `DatePicker` child. Height 28px, `shadow.control`, `radius` 4px.
|
|
8
8
|
|
|
9
9
|
```tsx
|
|
10
10
|
<Button>Save</Button>
|
|
@@ -28,7 +28,7 @@ Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`,
|
|
|
28
28
|
| `onClick` | Still supported. React Aria also exposes `onPress` |
|
|
29
29
|
| `children` | Text is wrapped for cap-height trim; SVG icons sit beside text. Icon-only needs `aria-label` |
|
|
30
30
|
|
|
31
|
-
Primary: fill step 9, hover 10, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; hover is `l - 0.04`; text is white or near-black from lightness.
|
|
31
|
+
Primary: fill step 9, hover 10, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; hover is `l - 0.04`; text is white or near-black from lightness. Its raised shadow strengthens the white inset highlight and tints the tight outer edge from the fill. Quiet: no fill; `variantColor` tints the label/icon. Hover/press mix `grayAlpha[9]` (or that color’s alpha 9, or the hex) into transparent at 6% light / 9% dark; press is 2×. Hover fills snap.
|
|
32
32
|
|
|
33
33
|
While a menu or overlay is open, the trigger keeps the active fill via RAC `data-pressed` / `aria-expanded` (same tokens as `:active`: `elementActive`, or primary step 10).
|
|
34
34
|
|
|
@@ -61,4 +61,3 @@ Full-viewport portal. `onClickOutside` fires when the backdrop itself is the mou
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Edge-anchored mobile nav is [`Drawer`](drawer.md), not this Dialog and not Overlay.
|
|
64
|
-
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Gallery: `/components/form-controls`. Import from `"maui"`. [Select](select.md) has its own page.
|
|
4
4
|
|
|
5
|
-
All fields are 28px tall, full width of the parent, `shadow.
|
|
5
|
+
All fields are 28px tall, full width of the parent, `shadow.control` except `QuietTextField`. They take React Aria field props (`value`, `onChange`, `placeholder`, `isDisabled`, `isInvalid`, `aria-label`, `id`, …).
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
<TextField aria-label="Title" placeholder="Title" value={v} onChange={setV} />
|
|
9
|
-
<SearchField aria-label="Search" value={q} onChange={setQ} /> // clear button when non-empty
|
|
8
|
+
<TextField aria-label="Title" placeholder="Title" value={v} onChange={setV} keyboardHint="T" />
|
|
9
|
+
<SearchField aria-label="Search" value={q} onChange={setQ} keyboardHint="/" /> // clear button when non-empty
|
|
10
10
|
<NumberField aria-label="Count" value={n} onChange={setN} minValue={0} maxValue={10} />
|
|
11
11
|
<QuietTextField aria-label="Filter" placeholder="Filter" value={f} onChange={setF} />
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
`NumberField` uses tabular numerals so the value does not shift as you step. Invalid (unfocused) adds a red 1px ring on top of `shadow.
|
|
14
|
+
`NumberField` uses tabular numerals so the value does not shift as you step. Invalid (unfocused) adds a red 1px ring on top of `shadow.control`. Placeholders are italic `gray[8]`. Cap width in the parent (`maxWidth: 240px` is the gallery default).
|
|
15
|
+
|
|
16
|
+
`TextField`, `SearchField`, and `QuietTextField` accept `keyboardHint`, a single-character shortcut rendered at the inline end while the field is unfocused. The hint is presentational; the consuming app owns the keyboard handler. `SearchField` hides it when the query is non-empty so the clear button can use the same space.
|
|
15
17
|
|
|
16
18
|
## `Checkbox`
|
|
17
19
|
|
|
@@ -12,15 +12,17 @@ Patterns, demo apps, and the gallery `Panel` preview frame are not package compo
|
|
|
12
12
|
|
|
13
13
|
## Shadows
|
|
14
14
|
|
|
15
|
-
Use
|
|
15
|
+
Use `shadow.control` for interactive control surfaces. It combines two inset
|
|
16
|
+
highlights with a compact outer edge. Use the three-level elevation scale for
|
|
17
|
+
other raised surfaces:
|
|
16
18
|
|
|
17
|
-
- `shadow.subtle` —
|
|
19
|
+
- `shadow.subtle` — cards and ordinary raised surfaces
|
|
18
20
|
- `shadow.medium` — tooltips and larger floating panels
|
|
19
21
|
- `shadow.strong` — dropdowns, popovers, and dominant overlays
|
|
20
22
|
|
|
21
23
|
All three include a 1px ring. Do not also apply `border()`, `borderColor.outline`, or another ring to the same element.
|
|
22
24
|
|
|
23
|
-
Buttons and form-control surfaces use `shadow.
|
|
25
|
+
Buttons and form-control surfaces use `shadow.control` by default. For compound controls, apply it once to the outer control boundary instead of to every internal button or segment.
|
|
24
26
|
|
|
25
27
|
## Focus
|
|
26
28
|
|
|
@@ -170,12 +170,14 @@ Controls typically use `sm`. Cards / sidebars use `lg`. Avatars use `circle`.
|
|
|
170
170
|
## Shadows and rings
|
|
171
171
|
|
|
172
172
|
```ts
|
|
173
|
-
shadow.
|
|
174
|
-
|
|
173
|
+
shadow.control // button/form-control style object
|
|
174
|
+
shadow.subtle | shadow.medium | shadow.strong // elevation style objects
|
|
175
|
+
shadowVars.control | .subtle | .medium | .strong // raw box-shadow strings
|
|
175
176
|
tintedSubtle(color) // subtle ring+blur tinted from a fill
|
|
176
177
|
```
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
`shadow.control` uses inset highlights and a compact outer edge. Every elevation
|
|
180
|
+
already includes a 1px ring. **Do not also `border()` the same element.**
|
|
179
181
|
|
|
180
182
|
`border(sides, color)`:
|
|
181
183
|
|
|
@@ -184,7 +186,7 @@ Every elevation already includes a 1px ring. **Do not also `border()` the same e
|
|
|
184
186
|
|
|
185
187
|
`borderColor.border` / `borderColor.outline` are the raw color strings (hairlines, `borderBottom`).
|
|
186
188
|
|
|
187
|
-
`focusRing(selector = "&:focus-visible", existingShadow?)` — Radix blue ring. Pass `shadowVars.
|
|
189
|
+
`focusRing(selector = "&:focus-visible", existingShadow?)` — Radix blue ring. Pass `shadowVars.control` as the second argument so a form control keeps its surface treatment while focused; use the matching elevation variable for other raised surfaces.
|
|
188
190
|
|
|
189
191
|
`visuallyHidden` — clip an input and keep the styled sibling visible (Checkbox / Switch / Radio).
|
|
190
192
|
|