@tanishqkancharla/maui 0.0.59 → 0.0.60
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 +10 -9
- package/dist/components/Dialog.js +3 -2
- package/dist/tokens/background.js +1 -1
- package/dist/tokens/shadow.d.ts +2 -2
- package/dist/tokens/shadow.js +5 -2
- package/package.json +1 -1
- package/skills/maui/references/components/buttons.md +1 -1
- package/skills/maui/references/tokens.md +1 -1
- package/src/components/Button.tsx +24 -20
- package/src/components/Dialog.tsx +3 -2
- package/src/pages/BackgroundColorTokenPage.tsx +2 -2
- package/src/pages/ButtonsPage.tsx +5 -2
- package/src/pages/ReadmePage.tsx +1 -1
- package/src/pages/ShadowTokenPage.tsx +2 -1
- package/src/tokens/background.ts +1 -1
- package/src/tokens/shadow.ts +5 -2
|
@@ -30,7 +30,7 @@ const disabledQuiet = {
|
|
|
30
30
|
backgroundColor: "transparent",
|
|
31
31
|
boxShadow: "none",
|
|
32
32
|
};
|
|
33
|
-
const buttonBaseClass = style(text({ size: "xs", fontWeight:
|
|
33
|
+
const buttonBaseClass = style(text({ size: "xs", fontWeight: 500, color: "highContrast" }), focusRing("&:focus-visible", buttonShadow), spacing.padding({ x: 6, y: 4 }), radius.sm, {
|
|
34
34
|
display: "inline-flex",
|
|
35
35
|
alignItems: "center",
|
|
36
36
|
justifyContent: "center",
|
|
@@ -65,7 +65,7 @@ const buttonTextClass = style({
|
|
|
65
65
|
minWidth: 0,
|
|
66
66
|
textBox: "trim-both cap alphabetic",
|
|
67
67
|
});
|
|
68
|
-
const smallButtonClass = style(text({ size: "2xs", fontWeight:
|
|
68
|
+
const smallButtonClass = style(text({ size: "2xs", fontWeight: 500, color: "highContrast" }), {
|
|
69
69
|
height: controlSize.smHeight,
|
|
70
70
|
gap: spacing.value(1),
|
|
71
71
|
paddingInline: spacing.value(4),
|
|
@@ -146,9 +146,9 @@ function onSolidText(color) {
|
|
|
146
146
|
function fillShade(color, lightnessOffset, alpha = 1) {
|
|
147
147
|
return `oklch(from ${color} calc(l - ${lightnessOffset}) c h / ${alpha})`;
|
|
148
148
|
}
|
|
149
|
-
/**
|
|
150
|
-
function primaryButtonShadow(color) {
|
|
151
|
-
return `inset 0
|
|
149
|
+
/** Dark-only top highlight, a fill-tinted lift (half strength in dark mode), and an edge painted beneath the lift: the fill's alpha step 6 in light mode, a near-black tint of the fill in dark mode. */
|
|
150
|
+
function primaryButtonShadow(color, lightEdge) {
|
|
151
|
+
return `inset 0 .5px light-dark(transparent, rgb(255 255 255 / 0.15)), 0 1px 2px light-dark(${fillShade(color, 0, 0.35)}, ${fillShade(color, 0, 0.18)}), 0 3px 8px -3px light-dark(${fillShade(color, 0, 0.6)}, ${fillShade(color, 0, 0.3)}), 0 0 0 .5px light-dark(${lightEdge}, oklch(from ${color} 0.18 calc(c * 0.5) h))`;
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
154
|
* Diagonal from a slightly darker, more saturated, warmer-rotated fill to the
|
|
@@ -159,8 +159,8 @@ function primaryButtonGradient(color) {
|
|
|
159
159
|
}
|
|
160
160
|
/** Hover lightens the top of the gradient instead of replacing the fill. */
|
|
161
161
|
const primaryHoverSheen = `linear-gradient(to bottom, rgb(255 255 255 / 0.12), rgb(255 255 255 / 0))`;
|
|
162
|
-
const primaryClass = memoize((fill, textColor) => {
|
|
163
|
-
const primaryShadow = primaryButtonShadow(fill);
|
|
162
|
+
const primaryClass = memoize((fill, textColor, lightEdge) => {
|
|
163
|
+
const primaryShadow = primaryButtonShadow(fill, lightEdge);
|
|
164
164
|
const gradient = primaryButtonGradient(fill);
|
|
165
165
|
return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
|
|
166
166
|
color: textColor,
|
|
@@ -177,13 +177,14 @@ const coloredButtonClass = memoize((variant, color) => {
|
|
|
177
177
|
if (isCssColor(color)) {
|
|
178
178
|
const fill = opaqueColor(color);
|
|
179
179
|
if (variant === "primary") {
|
|
180
|
-
|
|
180
|
+
// No alpha scale for a custom fill; approximate step A6 with the fill at 35%.
|
|
181
|
+
return primaryClass(fill, onSolidText(fill), fillShade(fill, 0, 0.35));
|
|
181
182
|
}
|
|
182
183
|
return quietClass(fill, darkerFill(fill), fill);
|
|
183
184
|
}
|
|
184
185
|
const scale = paletteFill(color);
|
|
185
186
|
if (variant === "primary") {
|
|
186
|
-
return primaryClass(scale[9], darkTextOnSolid.has(color) ? scale[12] : "white");
|
|
187
|
+
return primaryClass(scale[9], darkTextOnSolid.has(color) ? scale[12] : "white", paletteAlpha(color)[6]);
|
|
187
188
|
}
|
|
188
189
|
return quietClass(scale[11], scale[12], paletteAlpha(color)[9]);
|
|
189
190
|
});
|
|
@@ -3,7 +3,7 @@ import { useCallback, useState, } from "react";
|
|
|
3
3
|
import { AnimatePresence, motion, useReducedMotion, } from "motion/react";
|
|
4
4
|
import { Dialog as AriaDialog, Heading, Modal, ModalOverlay, } from "react-aria-components";
|
|
5
5
|
import { style, useStyles } from "purse-styles";
|
|
6
|
-
import {
|
|
6
|
+
import { colors } from "../tokens/colors";
|
|
7
7
|
import { border } from "../tokens/borders";
|
|
8
8
|
import { overlayMotion } from "../tokens/motion";
|
|
9
9
|
import { radius } from "../tokens/radius";
|
|
@@ -43,7 +43,8 @@ const overlay = style({
|
|
|
43
43
|
overflow: "auto",
|
|
44
44
|
overscrollBehavior: "contain",
|
|
45
45
|
});
|
|
46
|
-
const modalBase = style(
|
|
46
|
+
const modalBase = style(border([], "outline"), radius.lg, {
|
|
47
|
+
backgroundColor: colors.gray[2],
|
|
47
48
|
width: "min(440px, calc(100vw - 32px))",
|
|
48
49
|
maxWidth: "100%",
|
|
49
50
|
maxHeight: "calc(100dvh - 32px)",
|
package/dist/tokens/shadow.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const shadowVars: import("purse-styles").VariableGroup<{
|
|
2
2
|
readonly control: {
|
|
3
|
-
readonly default:
|
|
4
|
-
readonly ":root[data-theme=\"dark\"]": "inset 0
|
|
3
|
+
readonly default: `0 .5px 1px #00000018, 0 0 4px -1px #0001, 0 0 0 .5px ${string}`;
|
|
4
|
+
readonly ":root[data-theme=\"dark\"]": "inset 0 .5px rgb(255 255 255 / 0.09), 0 1px .5px #00000018, 0 0 3px -1px #0005, 0 0 0 .5px rgb(0 0 0 / 0.8)";
|
|
5
5
|
};
|
|
6
6
|
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`;
|
|
7
7
|
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`;
|
package/dist/tokens/shadow.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineVars, style } from "purse-styles";
|
|
2
2
|
import { DARK_THEME } from "../theme/dataTheme";
|
|
3
|
+
import { colors } from "./colors";
|
|
3
4
|
const shadowParams = defineVars({
|
|
4
5
|
foregroundRgb: {
|
|
5
6
|
default: "0, 0, 0",
|
|
@@ -14,8 +15,10 @@ const rgb = shadowParams.foregroundRgb;
|
|
|
14
15
|
const blur = shadowParams.blurOpacity;
|
|
15
16
|
export const shadowVars = defineVars({
|
|
16
17
|
control: {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
// 0.5px edge listed last, so it paints beneath the drop: light reads as a
|
|
19
|
+
// gray A6 border; dark is near-black so the control looks cut into the surface.
|
|
20
|
+
default: `0 .5px 1px #00000018, 0 0 4px -1px #0001, 0 0 0 .5px ${colors.grayAlpha[6]}`,
|
|
21
|
+
[DARK_THEME]: `inset 0 .5px rgb(255 255 255 / 0.09), 0 1px .5px #00000018, 0 0 3px -1px #0005, 0 0 0 .5px rgb(0 0 0 / 0.8)`,
|
|
19
22
|
},
|
|
20
23
|
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`,
|
|
21
24
|
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`,
|
package/package.json
CHANGED
|
@@ -30,7 +30,7 @@ Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`,
|
|
|
30
30
|
| `onClick` | Still supported. React Aria also exposes `onPress` |
|
|
31
31
|
| `children` | Text is wrapped for cap-height trim; SVG icons sit beside text. Icon-only needs `aria-label` |
|
|
32
32
|
|
|
33
|
-
Primary: fill step 9, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; text is white or near-black from lightness. The fill is a diagonal gradient (`to top left`) from the fill shifted by OKLCH `l − 0.027`, `c × 1.05`, `h − 6.8` to the fill itself.
|
|
33
|
+
Primary: fill step 9, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; text is white or near-black from lightness. The fill is a diagonal gradient (`to top left`) from the fill shifted by OKLCH `l − 0.027`, `c × 1.05`, `h − 6.8` to the fill itself. In dark mode the shadow has a 0.5px white top highlight at 15% (`shadow.control` uses 9%); light mode has none. The shadow tints a soft lift from the fill, at half strength in dark mode, and adds a 0.5px edge painted beneath the lift: the fill color’s alpha step 6 in light mode (a custom CSS fill uses the fill at 35%); in dark mode a near-black tint of the fill (OKLCH lightness 0.18, half chroma) — no neutral gray. Hover layers a white-to-transparent sheen over the gradient; disabled drops the gradient. 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.
|
|
34
34
|
|
|
35
35
|
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).
|
|
36
36
|
|
|
@@ -178,7 +178,7 @@ shadowVars.control | .subtle | .medium | .strong // raw box-shadow strings
|
|
|
178
178
|
tintedSubtle(color) // subtle ring+blur tinted from a fill
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
`shadow.control`
|
|
181
|
+
`shadow.control` is a small drop over a 0.5px edge: gray A6 (`grayAlpha[6]`) in light mode, 80% black in dark mode so the control looks cut into the surface. Dark mode also adds a 0.5px top highlight. Every elevation
|
|
182
182
|
already includes a 1px ring. **Do not also `border()` the same element.**
|
|
183
183
|
|
|
184
184
|
`border(sides, color)`:
|
|
@@ -44,7 +44,7 @@ const disabledQuiet = {
|
|
|
44
44
|
} as const
|
|
45
45
|
|
|
46
46
|
const buttonBaseClass = style(
|
|
47
|
-
text({ size: "xs", fontWeight:
|
|
47
|
+
text({ size: "xs", fontWeight: 500, color: "highContrast" }),
|
|
48
48
|
focusRing("&:focus-visible", buttonShadow),
|
|
49
49
|
spacing.padding({ x: 6, y: 4 }),
|
|
50
50
|
radius.sm,
|
|
@@ -87,7 +87,7 @@ const buttonTextClass = style({
|
|
|
87
87
|
})
|
|
88
88
|
|
|
89
89
|
const smallButtonClass = style(
|
|
90
|
-
text({ size: "2xs", fontWeight:
|
|
90
|
+
text({ size: "2xs", fontWeight: 500, color: "highContrast" }),
|
|
91
91
|
{
|
|
92
92
|
height: controlSize.smHeight,
|
|
93
93
|
gap: spacing.value(1),
|
|
@@ -193,9 +193,9 @@ function fillShade(color: string, lightnessOffset: number, alpha = 1) {
|
|
|
193
193
|
return `oklch(from ${color} calc(l - ${lightnessOffset}) c h / ${alpha})`
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
/**
|
|
197
|
-
function primaryButtonShadow(color: string) {
|
|
198
|
-
return `inset 0
|
|
196
|
+
/** Dark-only top highlight, a fill-tinted lift (half strength in dark mode), and an edge painted beneath the lift: the fill's alpha step 6 in light mode, a near-black tint of the fill in dark mode. */
|
|
197
|
+
function primaryButtonShadow(color: string, lightEdge: string) {
|
|
198
|
+
return `inset 0 .5px light-dark(transparent, rgb(255 255 255 / 0.15)), 0 1px 2px light-dark(${fillShade(color, 0, 0.35)}, ${fillShade(color, 0, 0.18)}), 0 3px 8px -3px light-dark(${fillShade(color, 0, 0.6)}, ${fillShade(color, 0, 0.3)}), 0 0 0 .5px light-dark(${lightEdge}, oklch(from ${color} 0.18 calc(c * 0.5) h))`
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
/**
|
|
@@ -209,27 +209,30 @@ function primaryButtonGradient(color: string) {
|
|
|
209
209
|
/** Hover lightens the top of the gradient instead of replacing the fill. */
|
|
210
210
|
const primaryHoverSheen = `linear-gradient(to bottom, rgb(255 255 255 / 0.12), rgb(255 255 255 / 0))`
|
|
211
211
|
|
|
212
|
-
const primaryClass = memoize(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
})
|
|
212
|
+
const primaryClass = memoize(
|
|
213
|
+
(fill: string, textColor: string, lightEdge: string) => {
|
|
214
|
+
const primaryShadow = primaryButtonShadow(fill, lightEdge)
|
|
215
|
+
const gradient = primaryButtonGradient(fill)
|
|
216
|
+
return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
|
|
217
|
+
color: textColor,
|
|
218
|
+
backgroundColor: fill,
|
|
219
|
+
backgroundImage: gradient,
|
|
220
|
+
boxShadow: primaryShadow,
|
|
221
|
+
"&:hover:not(:disabled)": {
|
|
222
|
+
backgroundImage: `${primaryHoverSheen}, ${gradient}`,
|
|
223
|
+
},
|
|
224
|
+
"&:disabled": disabledRaised,
|
|
225
|
+
})
|
|
226
|
+
},
|
|
227
|
+
)
|
|
226
228
|
|
|
227
229
|
const coloredButtonClass = memoize(
|
|
228
230
|
(variant: "primary" | "quiet", color: ButtonVariantColor) => {
|
|
229
231
|
if (isCssColor(color)) {
|
|
230
232
|
const fill = opaqueColor(color)
|
|
231
233
|
if (variant === "primary") {
|
|
232
|
-
|
|
234
|
+
// No alpha scale for a custom fill; approximate step A6 with the fill at 35%.
|
|
235
|
+
return primaryClass(fill, onSolidText(fill), fillShade(fill, 0, 0.35))
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
return quietClass(fill, darkerFill(fill), fill)
|
|
@@ -240,6 +243,7 @@ const coloredButtonClass = memoize(
|
|
|
240
243
|
return primaryClass(
|
|
241
244
|
scale[9],
|
|
242
245
|
darkTextOnSolid.has(color) ? scale[12] : "white",
|
|
246
|
+
paletteAlpha(color)[6],
|
|
243
247
|
)
|
|
244
248
|
}
|
|
245
249
|
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
ModalOverlay,
|
|
18
18
|
} from "react-aria-components"
|
|
19
19
|
import { style, useStyles } from "purse-styles"
|
|
20
|
-
import {
|
|
20
|
+
import { colors } from "../tokens/colors"
|
|
21
21
|
import { border } from "../tokens/borders"
|
|
22
22
|
import { overlayMotion } from "../tokens/motion"
|
|
23
23
|
import { radius } from "../tokens/radius"
|
|
@@ -81,7 +81,8 @@ const overlay = style({
|
|
|
81
81
|
overscrollBehavior: "contain",
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
const modalBase = style(
|
|
84
|
+
const modalBase = style(border([], "outline"), radius.lg, {
|
|
85
|
+
backgroundColor: colors.gray[2],
|
|
85
86
|
width: "min(440px, calc(100vw - 32px))",
|
|
86
87
|
maxWidth: "100%",
|
|
87
88
|
maxHeight: "calc(100dvh - 32px)",
|
|
@@ -62,11 +62,11 @@ export function BackgroundColorTokenPage() {
|
|
|
62
62
|
<Code>background.element</Code>
|
|
63
63
|
</TableCell>
|
|
64
64
|
<TableCell>
|
|
65
|
-
<Code>#ffffff</Code> / <Code>colors.gray[
|
|
65
|
+
<Code>#ffffff</Code> / <Code>colors.gray[3]</Code>
|
|
66
66
|
</TableCell>
|
|
67
67
|
<TableCell>
|
|
68
68
|
Inputs and slightly raised controls. Matches app white in
|
|
69
|
-
light;
|
|
69
|
+
light; two steps lighter than the canvas in dark.
|
|
70
70
|
</TableCell>
|
|
71
71
|
</TableRow>
|
|
72
72
|
<TableRow>
|
|
@@ -86,8 +86,11 @@ export function ButtonsPage() {
|
|
|
86
86
|
<Code>variantColor</Code> (a palette name, default <Code>"accent"</Code>
|
|
87
87
|
). Pass a hex or <Code>rgb()</Code> string to use that color as the fill
|
|
88
88
|
(alpha is dropped). The fill is a subtle diagonal gradient from a
|
|
89
|
-
slightly darker, warmer shade of that color, and the
|
|
90
|
-
|
|
89
|
+
slightly darker, warmer shade of that color, and the lift shadow is
|
|
90
|
+
tinted from it; the lift is half as strong in dark mode. The 0.5px edge
|
|
91
|
+
is alpha step 6 of that color in light mode and a near-black tint of the
|
|
92
|
+
fill in dark mode. Dark mode adds a 0.5px white top highlight (15% on
|
|
93
|
+
primary, 9% on default buttons). Hover adds a white sheen at the top.
|
|
91
94
|
</P>
|
|
92
95
|
<Flex row alignItems="center" gap={4} style={{ flexWrap: "wrap" }}>
|
|
93
96
|
<Button variant="primary">Save</Button>
|
package/src/pages/ReadmePage.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { H2, H3, Li, Link, P, Ul } from "../components/Typography"
|
|
|
6
6
|
export function ReadmePage() {
|
|
7
7
|
return (
|
|
8
8
|
<Prose style={{ marginBottom: "32px" }}>
|
|
9
|
-
<H2>
|
|
9
|
+
<H2>README</H2>
|
|
10
10
|
<P>
|
|
11
11
|
Maui is an open-source, TypeScript-first design system styled with{" "}
|
|
12
12
|
<Link href="https://www.npmjs.com/package/purse-styles">
|
|
@@ -27,7 +27,8 @@ export function ShadowTokenPage() {
|
|
|
27
27
|
<Prose style={{ marginBottom: "32px" }}>
|
|
28
28
|
<H2>Shadows</H2>
|
|
29
29
|
<P>
|
|
30
|
-
Controls use
|
|
30
|
+
Controls use a small drop over a 0.5px edge: gray A6 in light mode,
|
|
31
|
+
near-black in dark mode, plus a top highlight in dark mode. Other surfaces
|
|
31
32
|
use Craft's three-level elevation stack: a
|
|
32
33
|
foreground-colored 1px ring plus progressively deeper black blur layers.
|
|
33
34
|
Blur opacity is 0.06 in light mode and 0.12 in dark mode.
|
package/src/tokens/background.ts
CHANGED
package/src/tokens/shadow.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineVars, style } from "purse-styles"
|
|
2
2
|
import { DARK_THEME } from "../theme/dataTheme"
|
|
3
|
+
import { colors } from "./colors"
|
|
3
4
|
|
|
4
5
|
const shadowParams = defineVars({
|
|
5
6
|
foregroundRgb: {
|
|
@@ -17,8 +18,10 @@ const blur = shadowParams.blurOpacity
|
|
|
17
18
|
|
|
18
19
|
export const shadowVars = defineVars({
|
|
19
20
|
control: {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
// 0.5px edge listed last, so it paints beneath the drop: light reads as a
|
|
22
|
+
// gray A6 border; dark is near-black so the control looks cut into the surface.
|
|
23
|
+
default: `0 .5px 1px #00000018, 0 0 4px -1px #0001, 0 0 0 .5px ${colors.grayAlpha[6]}`,
|
|
24
|
+
[DARK_THEME]: `inset 0 .5px rgb(255 255 255 / 0.09), 0 1px .5px #00000018, 0 0 3px -1px #0005, 0 0 0 .5px rgb(0 0 0 / 0.8)`,
|
|
22
25
|
},
|
|
23
26
|
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`,
|
|
24
27
|
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`,
|