@tanishqkancharla/maui 0.0.70 → 0.0.72
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 +1 -1
- package/dist/components/ListBox.js +4 -1
- package/dist/components/Table.js +4 -3
- package/dist/components/navigationItem.js +4 -1
- package/package.json +1 -1
- package/skills/maui/references/components/buttons.md +4 -4
- package/skills/maui/references/components/menu.md +1 -1
- package/skills/maui/references/design_conventions.md +39 -10
- package/skills/maui/references/tokens.md +18 -21
- package/src/apps/Calendar/Calendar.tsx +1 -1
- package/src/components/Button.tsx +1 -1
- package/src/components/ListBox.tsx +4 -1
- package/src/components/Table.tsx +7 -2
- package/src/components/navigationItem.ts +4 -1
- package/src/pages/BordersTokenPage.tsx +57 -24
- package/src/patterns/Sidebar.tsx +1 -1
|
@@ -225,7 +225,7 @@ const coloredButtonClass = memoize((variant, color) => {
|
|
|
225
225
|
}
|
|
226
226
|
const scale = paletteFill(color);
|
|
227
227
|
if (variant === "primary") {
|
|
228
|
-
return primaryClass(scale[9], darkTextOnSolid.has(color) ? scale[
|
|
228
|
+
return primaryClass(scale[9], darkTextOnSolid.has(color) ? onSolidText(scale[9]) : "white", paletteAlpha(color)[6]);
|
|
229
229
|
}
|
|
230
230
|
return quietClass(scale[11], scale[12], variant === "quiet" ? paletteAlpha(color)[9] : undefined);
|
|
231
231
|
});
|
|
@@ -32,9 +32,12 @@ const item = style(spacing.padding({ x: 4, y: 2 }), text({ size: "sm", fontWeigh
|
|
|
32
32
|
background: backgroundColor.elementHover,
|
|
33
33
|
},
|
|
34
34
|
"&[data-selected]": {
|
|
35
|
-
color: colors.accent[
|
|
35
|
+
color: colors.accent[11],
|
|
36
36
|
fontWeight: 500,
|
|
37
37
|
},
|
|
38
|
+
"&[data-selected]:not([data-disabled]):is(:hover, [data-hovered], [data-focused])": {
|
|
39
|
+
color: colors.accent[12],
|
|
40
|
+
},
|
|
38
41
|
"&[data-selected]::after": {
|
|
39
42
|
content: '"✓"',
|
|
40
43
|
position: "absolute",
|
package/dist/components/Table.js
CHANGED
|
@@ -69,7 +69,7 @@ const row = style({
|
|
|
69
69
|
"&[data-selection-mode][data-selected] td": {
|
|
70
70
|
backgroundColor: colors.accent[3],
|
|
71
71
|
},
|
|
72
|
-
"&[data-disabled]": {
|
|
72
|
+
"&[data-disabled] td": {
|
|
73
73
|
color: colors.gray[8],
|
|
74
74
|
},
|
|
75
75
|
});
|
|
@@ -105,11 +105,11 @@ const selectionCheck = style({
|
|
|
105
105
|
height: "10px",
|
|
106
106
|
paddingTop: "0.5px",
|
|
107
107
|
paddingLeft: "0.5px",
|
|
108
|
-
fill:
|
|
108
|
+
fill: "white",
|
|
109
109
|
opacity: 0,
|
|
110
110
|
transform: "scale(0)",
|
|
111
111
|
});
|
|
112
|
-
const selectionCheckbox = style(flex({ alignItems: "center", justifyContent: "center" }), focusRing(`&[data-focus-visible] ${selectionBox}`, shadowVars.control), {
|
|
112
|
+
const selectionCheckbox = style(flex({ alignItems: "center", justifyContent: "center" }), focusRing(`&[data-focus-visible] ${selectionBox}`, shadowVars.control), focusRing(`&[data-selected][data-focus-visible] ${selectionBox}, &[data-indeterminate][data-focus-visible] ${selectionBox}`, shadowVars.selectedControl), {
|
|
113
113
|
position: "relative",
|
|
114
114
|
width: "fit-content",
|
|
115
115
|
cursor: "default",
|
|
@@ -129,6 +129,7 @@ const selectionCheckbox = style(flex({ alignItems: "center", justifyContent: "ce
|
|
|
129
129
|
},
|
|
130
130
|
[`&[data-selected] ${selectionBox}, &[data-indeterminate] ${selectionBox}`]: {
|
|
131
131
|
backgroundColor: colors.accent[9],
|
|
132
|
+
boxShadow: shadowVars.selectedControl,
|
|
132
133
|
},
|
|
133
134
|
[`&[data-hovered][data-selected] ${selectionBox}, &[data-hovered][data-indeterminate] ${selectionBox}`]: {
|
|
134
135
|
backgroundColor: surfaceWash(colors.gray[12], surfaceMixPercent.hover, colors.accent[9]),
|
|
@@ -13,7 +13,10 @@ export const navigationItem = style(spacing.padding({ x: 4, y: 2 }), text({ size
|
|
|
13
13
|
background: backgroundColor.elementHover,
|
|
14
14
|
},
|
|
15
15
|
"&[aria-current='page']": {
|
|
16
|
-
color: colors.accent[
|
|
16
|
+
color: colors.accent[11],
|
|
17
17
|
fontWeight: 500,
|
|
18
18
|
},
|
|
19
|
+
"&[aria-current='page']:hover": {
|
|
20
|
+
color: colors.accent[12],
|
|
21
|
+
},
|
|
19
22
|
});
|
package/package.json
CHANGED
|
@@ -29,16 +29,16 @@ Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`,
|
|
|
29
29
|
| `variant` | `"default"` (raised element) \| `"quiet"` (transparent, hover wash) \| `"text"` (transparent, color-only hover) \| `"primary"` (solid fill) |
|
|
30
30
|
| `variantColor` | Palette name or opaque `#hex` / `rgb(...)`. Primary defaults to `"accent"`; quiet and text default to gray and tint the label/icon when set |
|
|
31
31
|
| `isDisabled` | React Aria name. Maps to native `disabled`. **No `disabled` React prop** |
|
|
32
|
-
| `isPending` | Shows a current-color indicator beside visible content
|
|
32
|
+
| `isPending` | Shows a current-color indicator beside visible content. Blocks activation and suppresses hover fill while retaining focusability and variant color. Controlled by the caller, not by returned promises |
|
|
33
33
|
| `pendingLabel` | Optional visible string replacing children while pending (e.g. `"Saving…"`). Omit to keep children; provide it to replace an icon with progress text |
|
|
34
34
|
| `onClick` | Still supported. React Aria also exposes `onPress` |
|
|
35
35
|
| `children` | Text is wrapped for cap-height trim; SVG icons sit beside text. Icon-only needs `aria-label` |
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Use primary for an emphasized action, default for a raised neutral control, quiet for a low-emphasis action, and text for an inline-style action without a hover fill. Let Button manage its palette and state colors rather than rebuilding the fill or shadow. For exact treatment, inspect `src/components/Button.tsx` in the installed package as described in [design conventions](../design_conventions.md).
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Prefer plain labels (optionally alongside icons) so Button controls their foreground; see the [composition rule](../design_conventions.md#composition).
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
The text variant removes horizontal spacing and surface chrome while keeping the control height and focus ring. Use it when an action should align directly with surrounding text; hover changes the foreground, not the fill.
|
|
42
42
|
|
|
43
43
|
`useButton(props)` is exported for custom focus-tracked buttons; prefer `Button`.
|
|
44
44
|
|
|
@@ -23,7 +23,7 @@ Exactly two children on `MenuTrigger`: **trigger**, then **menu**. Use Maui `But
|
|
|
23
23
|
</MenuTrigger>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
`isDisabled` on the trigger button blocks open. Escape closes and focus returns to the trigger (`aria-expanded` tracks open state).
|
|
26
|
+
`isDisabled` on the trigger button blocks open. Escape closes and focus returns to the trigger (`aria-expanded` tracks open state).
|
|
27
27
|
|
|
28
28
|
`placement` defaults to `"bottom start"`. Popover uses `shadow.strong` via `CollectionPopover`.
|
|
29
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Maui design conventions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use the matching skill references for Maui's design choices, composition rules, and reusable token patterns. Inspect the installed package's source for tactical questions about props, defaults, state styling, or exact values. Source ships under `node_modules/maui/src/` with the documented npm alias, or `node_modules/@tanishqkancharla/maui/src/` without it. Follow components into their token definitions as needed; current implementation is not a guarantee of contrast or design intent.
|
|
4
4
|
|
|
5
5
|
Patterns, demo apps, and the gallery `Panel` preview frame are not package components. `Panel` is not on the `"maui"` barrel, in `src/components`, or in the JSX editor catalog. Rebuild reference recipes with barrel exports such as `Button`, `Flex`, and `text(...)`.
|
|
6
6
|
|
|
@@ -24,30 +24,59 @@ State styles key off React Aria attributes (`data-hovered`, `data-selected`, `da
|
|
|
24
24
|
|
|
25
25
|
## Composition
|
|
26
26
|
|
|
27
|
-
- Use Maui's `Text` component
|
|
27
|
+
- Use Maui's `Text` component for standalone inline typography; see [Text](components/text.md). `Text` and `text(...)` set an explicit color by default, overriding inherited variant, selected, or disabled colors. Prefer plain Button labels so the Button controls its foreground. Keep semantic elements such as headings, paragraphs, labels, and links for their respective roles.
|
|
28
28
|
- Hover backgrounds have no transitions. Hover fills such as `backgroundColor.elementHover`, quiet-button washes, and list or row highlights snap instantly. Do not animate `background` or `background-color` on hover with `motion.standard(...)` or a CSS `transition`. Other motion, including tooltips and transforms, is allowed.
|
|
29
29
|
- Simple apps default to a prose column: `style(proseContainerStyle, { width: "100%", marginInline: "auto" })`. `sizing.contentWidth` is that same style. Use it for single-column tools, settings, forms, and reading layouts. Multi-pane or full-bleed apps such as inboxes, calendars, and IDEs are exceptions.
|
|
30
30
|
- Design an intentional empty composition for every list, inbox, search result, or collection. Include copy and, when useful, an action. Do not leave a blank panel.
|
|
31
31
|
|
|
32
|
+
## Decorative edge strips
|
|
33
|
+
|
|
34
|
+
When a rounded surface uses a colored edge for emphasis, use a full-height rectangular strip inside a `position: relative; overflow: hidden` container. Keep the radius on the container, not the strip: a CSS left border follows the corners and curls inward. This is a composition recipe for custom UI, not a new component or a change to existing component borders. Keep using `border()` for outlines and dividers.
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const surface = style(radius.md, spacing.padding({ all: 6 }), {
|
|
38
|
+
position: "relative",
|
|
39
|
+
overflow: "hidden",
|
|
40
|
+
backgroundColor: colors.orange[3],
|
|
41
|
+
})
|
|
42
|
+
const strip = style({
|
|
43
|
+
position: "absolute",
|
|
44
|
+
insetBlock: 0,
|
|
45
|
+
left: 0,
|
|
46
|
+
width: "3px",
|
|
47
|
+
backgroundColor: colors.orange[9],
|
|
48
|
+
pointerEvents: "none",
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
function DecoratedSurface() {
|
|
52
|
+
const surfaceClass = useStyles(surface)
|
|
53
|
+
const stripClass = useStyles(strip)
|
|
54
|
+
return (
|
|
55
|
+
<div className={surfaceClass}>
|
|
56
|
+
<div aria-hidden="true" className={stripClass} />
|
|
57
|
+
<Text>Surface content</Text>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
32
63
|
## Shadows
|
|
33
64
|
|
|
34
|
-
Use `shadow.control` for interactive control surfaces.
|
|
35
|
-
highlights with a compact outer edge. Use the three-level elevation scale for
|
|
36
|
-
other raised surfaces:
|
|
65
|
+
Use `shadow.control` for interactive control surfaces. Use the three-level elevation scale for other raised surfaces:
|
|
37
66
|
|
|
38
67
|
- `shadow.subtle` — cards and ordinary raised surfaces
|
|
39
|
-
- `shadow.medium` —
|
|
68
|
+
- `shadow.medium` — larger floating panels
|
|
40
69
|
- `shadow.strong` — dropdowns, popovers, and dominant overlays
|
|
41
70
|
|
|
42
|
-
|
|
71
|
+
These shadows include their own edge or ring. Do not also apply `border()`, `borderColor.outline`, or another ring to the same element. Not every floating component uses elevation: reuse its exported styles rather than inferring a shadow from its placement.
|
|
43
72
|
|
|
44
73
|
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.
|
|
45
74
|
|
|
46
75
|
## Focus
|
|
47
76
|
|
|
48
|
-
`focusRing()`
|
|
77
|
+
Use `focusRing()` for Maui's theme-aware keyboard-focus treatment rather than adding a competing outline or box shadow.
|
|
49
78
|
|
|
50
|
-
**Control safe area.** Input and form-control shadows paint outside the component's layout box; reserve room for the resting, hovered, and keyboard-focused states, even when hover does not change the background.
|
|
79
|
+
**Control safe area.** Input and form-control shadows paint outside the component's layout box; reserve room for the resting, hovered, and keyboard-focused states, even when hover does not change the background. Use `spacing.padding(4)` as a starting inset between controls and clipping edges, and leave gaps between neighboring controls. Keep overflow visible on immediate wrappers; when a panel, rounded container, or scroll viewport must clip, put the safe inset inside that clipping boundary, not outside it as margin. Check the first and last controls at scroll edges, including checkbox, radio, switch, and slider focus states. Do not remove shadows or focus rings to fit a tight layout.
|
|
51
80
|
|
|
52
81
|
```tsx
|
|
53
82
|
// Keep the control's visual overflow inside the scrolling boundary.
|
|
@@ -61,7 +90,7 @@ Buttons and form-control surfaces use `shadow.control` by default. For compound
|
|
|
61
90
|
|
|
62
91
|
## Layout utilities
|
|
63
92
|
|
|
64
|
-
`Flex` and `Gap` take spacing scale steps
|
|
93
|
+
`Flex` and `Gap` take spacing scale steps, not raw pixels. For example, `<Flex row gap={4} padding={4}>` uses the same token for its gap and inset.
|
|
65
94
|
|
|
66
95
|
Put padding on `Flex` with `padding` or `p`, `px`, `py`, `pt`, `pr`, `pb`, and `pl`; there is no `Padding` component. `alignItems` and `justifyContent` use the same tokens as `flex()` (`start`, `center`, `end`, and others; `justifyContent` also supports `between`, `around`, and `evenly`). `background` is a surface token such as `app`, `element`, or `accent`. `Spacer` grows to fill leftover flex space. `Divider` is a horizontal rule.
|
|
67
96
|
|
|
@@ -56,12 +56,14 @@ function Row({ children }: { children: React.ReactNode }) {
|
|
|
56
56
|
|
|
57
57
|
`colors` is Radix Scales with a brand `accent` (teal in light, violet in dark). Each scale has steps **1–12**. Alpha scales are `colors.grayAlpha`, `colors.accentAlpha`, `colors.blueAlpha`, …
|
|
58
58
|
|
|
59
|
+
The scale roles below are a starting point, not a contrast guarantee or a state recipe for every component. Use text roles for labels rather than copying a solid fill's step. Check the actual foreground/background pair in light and dark themes, including selected and disabled states; preserve non-color cues for state and status.
|
|
60
|
+
|
|
59
61
|
| Steps | Use |
|
|
60
62
|
| --- | --- |
|
|
61
63
|
| 1–2 | App / subtle surfaces |
|
|
62
64
|
| 3–5 | Interactive washes |
|
|
63
65
|
| 6–8 | Borders / strong lines |
|
|
64
|
-
| 9–10 | Solid fills
|
|
66
|
+
| 9–10 | Solid fills |
|
|
65
67
|
| 11–12 | Text (11 secondary, 12 primary) |
|
|
66
68
|
|
|
67
69
|
```ts
|
|
@@ -74,6 +76,8 @@ colors.blueAlpha[8]
|
|
|
74
76
|
|
|
75
77
|
`colorNames` is `"accent"` plus every Radix palette name. `paletteNames` omits `"accent"`. `"blue"` as a `variantColor` is the Radix blue scale, not CSS `blue`.
|
|
76
78
|
|
|
79
|
+
Brand accent, semantic status, and decorative identity serve different roles. Prefer a component's variant or tone before assembling custom colors; follow the matching component's source when composing similar UI. For example, [Banner](components/banner.md) owns status tones, while [Avatar](components/avatar.md) uses paired decorative colors, not status meanings.
|
|
80
|
+
|
|
77
81
|
Prefer semantic surfaces over raw scale steps for chrome:
|
|
78
82
|
|
|
79
83
|
```ts
|
|
@@ -81,14 +85,14 @@ import { background, backgroundColor } from "maui"
|
|
|
81
85
|
|
|
82
86
|
backgroundColor.app // page
|
|
83
87
|
backgroundColor.element // raised control / card
|
|
84
|
-
backgroundColor.elementHover //
|
|
85
|
-
backgroundColor.elementActive //
|
|
88
|
+
backgroundColor.elementHover // hovered element surface
|
|
89
|
+
backgroundColor.elementActive // active element surface
|
|
86
90
|
background.app // style objects of the same values
|
|
87
91
|
background.element
|
|
88
92
|
background.accent // accent[9]
|
|
89
93
|
```
|
|
90
94
|
|
|
91
|
-
|
|
95
|
+
Alpha washes depend on their backdrop. A quiet control's transparent overlay is not interchangeable with `elementHover`, which is precomposed over the element surface. Reuse the matching component styles rather than recreating its wash. Hover fills snap instantly; see [design conventions](design_conventions.md#composition).
|
|
92
96
|
|
|
93
97
|
## Text
|
|
94
98
|
|
|
@@ -115,7 +119,7 @@ text({
|
|
|
115
119
|
|
|
116
120
|
Sizes step up on the **Large platform scale** (`:root[data-scale="large"]`, driven by `(pointer: coarse)` under the system preference): `md` is 14px/22px on desktop and 17px/26px on coarse pointers. See `useScale` / `ScaleProvider`.
|
|
117
121
|
|
|
118
|
-
- `lowContrast` → `gray[11]`, `highContrast` → `gray[12]`, `accent` → `accent[11]`, `onAccent` → white.
|
|
122
|
+
- `lowContrast` → `gray[11]`, `highContrast` → `gray[12]`, `accent` → `accent[11]`, `onAccent` → white. `onAccent` is literal white, not an adaptive foreground for every solid fill.
|
|
119
123
|
- UI sans: `fontFamily` (system ui-sans-serif stack). Mono: Commit Mono with `ss05` smart kerning (`monoFontStyle` / `monospace`).
|
|
120
124
|
- `tabular: true` → `font-variant-numeric: tabular-nums`. NumberField, TableHead, and TableCell turn this on by default. Body copy and Prose stay off.
|
|
121
125
|
- `baseTextStyle` is md / 400 / highContrast — already on `html, body`.
|
|
@@ -178,13 +182,12 @@ shadowVars.control | .subtle | .medium | .strong // raw box-shadow strings
|
|
|
178
182
|
tintedSubtle(color) // subtle ring+blur tinted from a fill
|
|
179
183
|
```
|
|
180
184
|
|
|
181
|
-
|
|
182
|
-
already includes a 1px ring. **Do not also `border()` the same element.**
|
|
185
|
+
For elevation choice, compound control boundaries, and avoiding doubled rings, follow the [shadow conventions](design_conventions.md#shadows).
|
|
183
186
|
|
|
184
187
|
`border(sides, color)`:
|
|
185
188
|
|
|
186
189
|
- `sides`: `[]` for all sides, or `"top" | "right" | "bottom" | "left"`
|
|
187
|
-
- `color`: `"border"` (
|
|
190
|
+
- `color`: `"border"` (subtle separator) | `"outline"` (stronger edge) | `"accent"` (accent edge)
|
|
188
191
|
|
|
189
192
|
`borderColor.border` / `borderColor.outline` are the raw color strings (hairlines, `borderBottom`).
|
|
190
193
|
|
|
@@ -195,20 +198,14 @@ already includes a 1px ring. **Do not also `border()` the same element.**
|
|
|
195
198
|
## Motion
|
|
196
199
|
|
|
197
200
|
```ts
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
overlayMotion //
|
|
201
|
+
motion.standard("opacity", "transform") // shared transition style
|
|
202
|
+
motionDurationMs // standard duration
|
|
203
|
+
motionStreamDurationMs // streaming text duration
|
|
204
|
+
motionEasing // standard easing
|
|
205
|
+
overlayMotion // overlay and reduced-motion presets
|
|
203
206
|
```
|
|
204
207
|
|
|
205
|
-
Use `motion.standard` for opacity, transform, box-shadow, color
|
|
206
|
-
|
|
207
|
-
`overlayMotion` defines tooltip enter (120ms entrance curve), tooltip exit
|
|
208
|
-
(80ms ease-in), dialog enter (200ms entrance curve), dialog exit (120ms
|
|
209
|
-
ease-in), backdrop (100ms), and opacity-only reduced motion (80ms). Crossfade
|
|
210
|
-
enter is a 0.3s spring (`bounce` 0.2); exit uses `motionDurationMs` /
|
|
211
|
-
ease-in-out. Streaming markdown uses `motionStreamDurationMs`.
|
|
208
|
+
Use `motion.standard` for opacity, transform, box-shadow, and color, not hover fills; see [design conventions](design_conventions.md#composition). Reuse `overlayMotion` for custom overlay enter/exit and reduced-motion handling; inspect the matching component for how it applies those presets.
|
|
212
209
|
|
|
213
210
|
## Sizing and icons
|
|
214
211
|
|
|
@@ -229,7 +226,7 @@ Stroke/fill is `currentColor`. Pass `size` on the icon component; optional `widt
|
|
|
229
226
|
|
|
230
227
|
## Avatar tokens
|
|
231
228
|
|
|
232
|
-
`avatar.green` / `avatar.orange` / `avatar.pink` each
|
|
229
|
+
`avatar.green` / `avatar.orange` / `avatar.pink` each provide a paired `background` and `foreground`; keep the pair together when using these decorative tokens. Prefer `Avatar` for identity UI rather than choosing its colors manually.
|
|
233
230
|
|
|
234
231
|
## Prose tokens
|
|
235
232
|
|
|
@@ -1463,7 +1463,7 @@ const miniDayClass = style(
|
|
|
1463
1463
|
text({ size: "xs", fontWeight: 400, color: "highContrast" }),
|
|
1464
1464
|
radius.circle,
|
|
1465
1465
|
focusRing(),
|
|
1466
|
-
motion.standard("
|
|
1466
|
+
motion.standard("color"),
|
|
1467
1467
|
{
|
|
1468
1468
|
display: "grid",
|
|
1469
1469
|
placeItems: "center",
|
|
@@ -303,7 +303,7 @@ const coloredButtonClass = memoize(
|
|
|
303
303
|
if (variant === "primary") {
|
|
304
304
|
return primaryClass(
|
|
305
305
|
scale[9],
|
|
306
|
-
darkTextOnSolid.has(color) ? scale[
|
|
306
|
+
darkTextOnSolid.has(color) ? onSolidText(scale[9]) : "white",
|
|
307
307
|
paletteAlpha(color)[6],
|
|
308
308
|
)
|
|
309
309
|
}
|
|
@@ -41,9 +41,12 @@ const item = style(
|
|
|
41
41
|
background: backgroundColor.elementHover,
|
|
42
42
|
},
|
|
43
43
|
"&[data-selected]": {
|
|
44
|
-
color: colors.accent[
|
|
44
|
+
color: colors.accent[11],
|
|
45
45
|
fontWeight: 500,
|
|
46
46
|
},
|
|
47
|
+
"&[data-selected]:not([data-disabled]):is(:hover, [data-hovered], [data-focused])": {
|
|
48
|
+
color: colors.accent[12],
|
|
49
|
+
},
|
|
47
50
|
"&[data-selected]::after": {
|
|
48
51
|
content: '"✓"',
|
|
49
52
|
position: "absolute",
|
package/src/components/Table.tsx
CHANGED
|
@@ -111,7 +111,7 @@ const row = style({
|
|
|
111
111
|
"&[data-selection-mode][data-selected] td": {
|
|
112
112
|
backgroundColor: colors.accent[3],
|
|
113
113
|
},
|
|
114
|
-
"&[data-disabled]": {
|
|
114
|
+
"&[data-disabled] td": {
|
|
115
115
|
color: colors.gray[8],
|
|
116
116
|
},
|
|
117
117
|
})
|
|
@@ -172,7 +172,7 @@ const selectionCheck = style({
|
|
|
172
172
|
height: "10px",
|
|
173
173
|
paddingTop: "0.5px",
|
|
174
174
|
paddingLeft: "0.5px",
|
|
175
|
-
fill:
|
|
175
|
+
fill: "white",
|
|
176
176
|
opacity: 0,
|
|
177
177
|
transform: "scale(0)",
|
|
178
178
|
})
|
|
@@ -180,6 +180,10 @@ const selectionCheck = style({
|
|
|
180
180
|
const selectionCheckbox = style(
|
|
181
181
|
flex({ alignItems: "center", justifyContent: "center" }),
|
|
182
182
|
focusRing(`&[data-focus-visible] ${selectionBox}`, shadowVars.control),
|
|
183
|
+
focusRing(
|
|
184
|
+
`&[data-selected][data-focus-visible] ${selectionBox}, &[data-indeterminate][data-focus-visible] ${selectionBox}`,
|
|
185
|
+
shadowVars.selectedControl,
|
|
186
|
+
),
|
|
183
187
|
{
|
|
184
188
|
position: "relative",
|
|
185
189
|
width: "fit-content",
|
|
@@ -201,6 +205,7 @@ const selectionCheckbox = style(
|
|
|
201
205
|
[`&[data-selected] ${selectionBox}, &[data-indeterminate] ${selectionBox}`]:
|
|
202
206
|
{
|
|
203
207
|
backgroundColor: colors.accent[9],
|
|
208
|
+
boxShadow: shadowVars.selectedControl,
|
|
204
209
|
},
|
|
205
210
|
[`&[data-hovered][data-selected] ${selectionBox}, &[data-hovered][data-indeterminate] ${selectionBox}`]:
|
|
206
211
|
{
|
|
@@ -18,8 +18,11 @@ export const navigationItem = style(
|
|
|
18
18
|
background: backgroundColor.elementHover,
|
|
19
19
|
},
|
|
20
20
|
"&[aria-current='page']": {
|
|
21
|
-
color: colors.accent[
|
|
21
|
+
color: colors.accent[11],
|
|
22
22
|
fontWeight: 500,
|
|
23
23
|
},
|
|
24
|
+
"&[aria-current='page']:hover": {
|
|
25
|
+
color: colors.accent[12],
|
|
26
|
+
},
|
|
24
27
|
},
|
|
25
28
|
)
|
|
@@ -36,11 +36,11 @@ const borderExamples = [
|
|
|
36
36
|
use: "Pane headers and horizontal section breaks.",
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
|
-
label:
|
|
39
|
+
label: "Clipped left accent",
|
|
40
40
|
sides: ["left"] as BorderSide[],
|
|
41
41
|
color: "accent" as const,
|
|
42
42
|
edgeWidth: "2px",
|
|
43
|
-
use: "
|
|
43
|
+
use: "Decorative emphasis on rounded surfaces.",
|
|
44
44
|
},
|
|
45
45
|
] as const
|
|
46
46
|
|
|
@@ -119,17 +119,46 @@ export function BordersTokenPage() {
|
|
|
119
119
|
</TableBody>
|
|
120
120
|
</Table>
|
|
121
121
|
|
|
122
|
-
<CodeBlock lang="
|
|
122
|
+
<CodeBlock lang="tsx">{`const panel = style(
|
|
123
123
|
background.element,
|
|
124
124
|
border([], "outline"),
|
|
125
125
|
radius.md,
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
{
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
const accentedSurface = style(
|
|
129
|
+
radius.md,
|
|
130
|
+
{
|
|
131
|
+
position: "relative",
|
|
132
|
+
overflow: "hidden",
|
|
133
|
+
padding: "16px",
|
|
134
|
+
backgroundColor: colors.gray[2],
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
const strip = style({
|
|
138
|
+
position: "absolute",
|
|
139
|
+
insetBlock: 0,
|
|
140
|
+
left: 0,
|
|
141
|
+
width: "2px",
|
|
142
|
+
backgroundColor: colors.accent[8],
|
|
143
|
+
pointerEvents: "none",
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
function DecoratedSurface() {
|
|
147
|
+
const surfaceClass = useStyles(accentedSurface)
|
|
148
|
+
const stripClass = useStyles(strip)
|
|
149
|
+
return (
|
|
150
|
+
<div className={surfaceClass}>
|
|
151
|
+
<div aria-hidden="true" className={stripClass} />
|
|
152
|
+
<Text>Surface content</Text>
|
|
153
|
+
</div>
|
|
154
|
+
)
|
|
155
|
+
}`}</CodeBlock>
|
|
156
|
+
<P>
|
|
157
|
+
For left-edge emphasis, clip a full-height rectangular accent inside
|
|
158
|
+
the rounded container instead of using a left border. The container
|
|
159
|
+
keeps its rounding without curling the accent inward. This is a
|
|
160
|
+
composition recipe, not a change to existing component borders.
|
|
161
|
+
</P>
|
|
133
162
|
|
|
134
163
|
<Panel style={{ marginTop: "16px" }}>
|
|
135
164
|
<div
|
|
@@ -166,20 +195,13 @@ function BorderExample(props: {
|
|
|
166
195
|
color: BorderColor
|
|
167
196
|
edgeWidth?: string
|
|
168
197
|
}) {
|
|
198
|
+
const leftAccent = props.sides.length === 1 && props.sides[0] === "left"
|
|
169
199
|
const className = useStyles(
|
|
170
|
-
border(props.sides, props.color),
|
|
171
|
-
props.edgeWidth
|
|
172
|
-
? style(
|
|
173
|
-
Object.fromEntries(
|
|
174
|
-
props.sides.map((side) => [
|
|
175
|
-
`border${capitalize(side)}Width`,
|
|
176
|
-
props.edgeWidth,
|
|
177
|
-
]),
|
|
178
|
-
),
|
|
179
|
-
)
|
|
180
|
-
: undefined,
|
|
200
|
+
!leftAccent && border(props.sides, props.color),
|
|
181
201
|
radius.md,
|
|
182
202
|
style({
|
|
203
|
+
position: "relative",
|
|
204
|
+
overflow: "hidden",
|
|
183
205
|
background: colors.gray[2],
|
|
184
206
|
padding: "16px",
|
|
185
207
|
textAlign: "center",
|
|
@@ -190,15 +212,26 @@ function BorderExample(props: {
|
|
|
190
212
|
|
|
191
213
|
return (
|
|
192
214
|
<div>
|
|
193
|
-
<div className={className}>
|
|
215
|
+
<div className={className}>
|
|
216
|
+
{leftAccent && (
|
|
217
|
+
<div
|
|
218
|
+
aria-hidden="true"
|
|
219
|
+
style={{
|
|
220
|
+
position: "absolute",
|
|
221
|
+
insetBlock: 0,
|
|
222
|
+
left: 0,
|
|
223
|
+
width: props.edgeWidth ?? "1px",
|
|
224
|
+
backgroundColor: colors.accent[8],
|
|
225
|
+
pointerEvents: "none",
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
{props.label}
|
|
230
|
+
</div>
|
|
194
231
|
</div>
|
|
195
232
|
)
|
|
196
233
|
}
|
|
197
234
|
|
|
198
|
-
function capitalize(value: string) {
|
|
199
|
-
return `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`
|
|
200
|
-
}
|
|
201
|
-
|
|
202
235
|
function SingleEdgeExample() {
|
|
203
236
|
const className = useStyles(
|
|
204
237
|
border(["bottom"], "border"),
|
package/src/patterns/Sidebar.tsx
CHANGED
|
@@ -142,7 +142,7 @@ const itemClass = style(navigationItem, focusRing("&:focus-visible"), {
|
|
|
142
142
|
backgroundColor: "transparent",
|
|
143
143
|
color: colors.gray[12],
|
|
144
144
|
"&[aria-current='page']": {
|
|
145
|
-
color: colors.accent[
|
|
145
|
+
color: colors.accent[11],
|
|
146
146
|
fontWeight: 500,
|
|
147
147
|
backgroundColor: "transparent",
|
|
148
148
|
},
|