@uxelle/skills 0.2.2 → 0.2.4
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/index.js +40 -35
- package/index.json +40 -35
- package/package.json +1 -1
- package/skills/uxelle-components/Icon.md +4 -4
- package/skills/uxelle-components/NavLink.md +2 -2
- package/skills/uxelle-components/Navigation.md +33 -23
- package/skills/uxelle-components/NavigationSide.md +3 -3
- package/skills/uxelle-components/NavigationSideItem.md +4 -4
- package/skills/uxelle-components/NavigationSideSubItem.md +3 -3
- package/skills/uxelle-components/SKILL.md +6 -1
- package/skills/uxelle-components/StatTile.md +13 -42
- package/skills/uxelle-components/getting-started.md +38 -7
- package/skills/uxelle-design-harness/SKILL.md +6 -2
- package/skills/uxelle-design-harness/a2ui.md +9 -0
- package/skills/uxelle-design-harness/how-to-accessibility.md +1 -1
- package/skills/uxelle-design-harness/how-to-host.md +5 -1
- package/skills/uxelle-design-harness/how-to-page-layout.md +4 -2
- package/skills/uxelle-design-harness/recipe-app-chrome.md +102 -25
- package/skills/uxelle-design-harness/recipe-dashboard-overview.md +4 -2
- package/skills/uxelle-design-harness/recipe-navigation.md +433 -0
- package/skills/uxelle-design-harness/recipe-stat-callouts.md +6 -4
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# Recipe: navigation
|
|
2
|
+
|
|
3
|
+
A shared **piece**: the header bar — brand lockup, optional category disclosures,
|
|
4
|
+
utilities, and a compact menu [`Sheet`](../uxelle-components/Sheet.md). Used by
|
|
5
|
+
[app-chrome](recipe-app-chrome.md) (product and marketing).
|
|
6
|
+
|
|
7
|
+
Composes [`Navigation`](../uxelle-components/Navigation.md),
|
|
8
|
+
[`Logo`](../uxelle-components/Logo.md), [`Text`](../uxelle-components/Text.md),
|
|
9
|
+
[`NavLink`](../uxelle-components/NavLink.md),
|
|
10
|
+
[`IconButton`](../uxelle-components/IconButton.md),
|
|
11
|
+
[`Sheet`](../uxelle-components/Sheet.md), [`Menu`](../uxelle-components/Menu.md),
|
|
12
|
+
[`List`](../uxelle-components/List.md), [`ListItem`](../uxelle-components/ListItem.md),
|
|
13
|
+
[`Layout`](../uxelle-components/Layout.md), [`Button`](../uxelle-components/Button.md),
|
|
14
|
+
and optionally [`Textfield`](../uxelle-components/Textfield.md),
|
|
15
|
+
[`LanguageSelector`](../uxelle-components/LanguageSelector.md),
|
|
16
|
+
[`Accordion`](../uxelle-components/Accordion.md),
|
|
17
|
+
[`NotificationBadge`](../uxelle-components/NotificationBadge.md), and
|
|
18
|
+
[`BannerAnnouncement`](../uxelle-components/BannerAnnouncement.md).
|
|
19
|
+
|
|
20
|
+
Slots are **open** — any node may live in any region. The defaults below are
|
|
21
|
+
generation defaults; a prompt may override them.
|
|
22
|
+
|
|
23
|
+
## When
|
|
24
|
+
|
|
25
|
+
The top-of-page wayfinding for an experience that uses header `Navigation`. Place
|
|
26
|
+
it in the app frame above `<main>` ([app-chrome](recipe-app-chrome.md)).
|
|
27
|
+
|
|
28
|
+
## When not
|
|
29
|
+
|
|
30
|
+
- Not an `AppChrome` / `NavigationBar` component to export from the customer app.
|
|
31
|
+
- Signed-in product destinations belong on
|
|
32
|
+
[`NavigationSide`](../uxelle-components/NavigationSide.md) — do not also put the
|
|
33
|
+
full IA in a header mega menu or a second hamburger. The header then holds
|
|
34
|
+
identity + utilities only.
|
|
35
|
+
- Focused flows (login, checkout, wizard): lockup only — no hamburger, no center
|
|
36
|
+
row, no footer.
|
|
37
|
+
- Breadcrumbs, table search, and data tabs stay in the **page column**
|
|
38
|
+
([recipe-data-table-page.md](recipe-data-table-page.md)), not in `Navigation`.
|
|
39
|
+
- A zip / region / “add store” strip above the bar is `BannerAnnouncement`, not a
|
|
40
|
+
fourth Navigation slot.
|
|
41
|
+
- Pass real slot content; omitting a slot (`undefined`) fills catalog demo
|
|
42
|
+
content. Pass **`null`** to drop a region. Do not pass `false` (empty wrapper
|
|
43
|
+
remains).
|
|
44
|
+
|
|
45
|
+
## Chrome pattern
|
|
46
|
+
|
|
47
|
+
Choose a frame **before** filling slots. Stacking every pattern is the usual miss.
|
|
48
|
+
|
|
49
|
+
- **Marketing / public** — header `Navigation` only (lockup + Sheet or mega menu).
|
|
50
|
+
This recipe’s default.
|
|
51
|
+
- **Signed-in product** — `NavigationSide` owns destinations; the header is
|
|
52
|
+
identity + utilities (search, account).
|
|
53
|
+
- **Hybrid portal** — lockup + hamburger Sheet, little or no center row, even on
|
|
54
|
+
desktop.
|
|
55
|
+
- **Focused flow** — lockup only; `centerSlot={null}`, no menu control.
|
|
56
|
+
|
|
57
|
+
## Regions
|
|
58
|
+
|
|
59
|
+
`leadingSlot` (brand lockup) · `centerSlot` (desktop category disclosures when
|
|
60
|
+
the IA is two or more levels deep; otherwise `null`, which leaves blank space
|
|
61
|
+
between leading and trailing) · `trailingSlot` (utilities + compact hamburger) ·
|
|
62
|
+
`bottomSlot` (`null` unless a persistent L2 module bar).
|
|
63
|
+
|
|
64
|
+
Optional `BannerAnnouncement` sits **above** `Navigation`, not in a slot.
|
|
65
|
+
|
|
66
|
+
## Lockup (`leadingSlot`)
|
|
67
|
+
|
|
68
|
+
- [`Logo`](../uxelle-components/Logo.md) when the experience has a mark
|
|
69
|
+
(`width={48}`). **Home is the mark**, not the whole lockup: wrap only `Logo` in
|
|
70
|
+
a plain `<a href="/" aria-label="Home">` and set `interactive`. Keep the mark
|
|
71
|
+
`aria-hidden` when title text is present.
|
|
72
|
+
- Two-line type — **no extra gap** (`gap={0}`). Condensed line boxes already
|
|
73
|
+
separate the lines; do not add Tight / `micro-2` between them
|
|
74
|
+
([recipe-data-table-page.md](recipe-data-table-page.md) identity cells):
|
|
75
|
+
- Top — `Text type="Condensed Alt"` `truncation`: the **site or experience
|
|
76
|
+
name**.
|
|
77
|
+
- Bottom — `Text type="Condensed"`: **location or a short descriptor**. Omit
|
|
78
|
+
the second line when there is nothing to say.
|
|
79
|
+
- Do not use Display or Body types in the lockup.
|
|
80
|
+
- Do **not** wrap the lockup in [`Link`](../uxelle-components/Link.md) or nest the
|
|
81
|
+
title in `<a>` / `uxl-link`. `Link` underlines at rest — that underline on
|
|
82
|
+
Condensed lockup text looks wrong.
|
|
83
|
+
- No mark → do not turn the title into a link just to get a home control.
|
|
84
|
+
- Do not also add a “Home” `NavLink` or Sheet row when the logo already goes home.
|
|
85
|
+
- The lockup is not the page `h1`.
|
|
86
|
+
|
|
87
|
+
## Center and overflow
|
|
88
|
+
|
|
89
|
+
- **One-level IA:** `centerSlot={null}`. Destinations live in the Sheet.
|
|
90
|
+
- **Two or more levels, from desktop up:** fill `centerSlot` with top-level
|
|
91
|
+
category controls that **click** open a disclosure panel (see
|
|
92
|
+
[IA depth](#ia-depth-what-is-inside-the-menu)). Hide that row below desktop.
|
|
93
|
+
Never show `centerSlot` and the compact hamburger together.
|
|
94
|
+
- About five to seven L1 items, then a `NavLink withMenu` labeled More
|
|
95
|
+
(overflow only). Do not wrap an unbounded row of categories.
|
|
96
|
+
|
|
97
|
+
## Menu control and Sheet
|
|
98
|
+
|
|
99
|
+
[`IconButton`](../uxelle-components/IconButton.md) `iconName="menu"`
|
|
100
|
+
`iconVariant="sharpUnfilled"` `emphasis="low"` `aria-label="Menu"` opens a
|
|
101
|
+
`Sheet` whose body is the destination tree.
|
|
102
|
+
|
|
103
|
+
Control the Sheet with `open` / `onOpenChange` (same idea as `NavigationSide`
|
|
104
|
+
`menuOpen` / `onMenuOpenChange`). Close it after a leaf: `onClick={() =>
|
|
105
|
+
setMenuOpen(false)}` on each destination `ListItem`. An uncontrolled Sheet
|
|
106
|
+
stays open after in-app navigation.
|
|
107
|
+
|
|
108
|
+
- **From desktop up:** the menu control may live in **any slot**. For two- and
|
|
109
|
+
three-level IA, omit it while `centerSlot` is visible — the compact control
|
|
110
|
+
is for below desktop.
|
|
111
|
+
- **Below desktop:** the **visible** menu control defaults to **`trailingSlot`**
|
|
112
|
+
(right edge). If a desktop menu is in `leadingSlot`, hide that instance below
|
|
113
|
+
desktop and show a trailing one — one visible hamburger per breakpoint, not
|
|
114
|
+
two. Switch with `useBreakpointUp("desktop")` (the band catalog chrome uses).
|
|
115
|
+
Do not use the tablet band for this — tablet would keep L1 links and the
|
|
116
|
+
hamburger on screen together.
|
|
117
|
+
- Do not leave only a leading hamburger glued to the title at compact widths.
|
|
118
|
+
- Do not add a header hamburger when the frame already uses `NavigationSide`
|
|
119
|
+
(it already owns the compact menu + right Sheet).
|
|
120
|
+
|
|
121
|
+
**Sheet `direction` follows the menu.** Open the panel from the same side as the
|
|
122
|
+
visible control:
|
|
123
|
+
|
|
124
|
+
- Menu on the **right** (`trailingSlot`) → `direction="Right"`.
|
|
125
|
+
- Menu on the **left** (`leadingSlot`) → `direction="Left"`.
|
|
126
|
+
- Ambiguous (e.g. center) → `Right`.
|
|
127
|
+
- Desktop leading + below-desktop trailing: switch `direction` with the same
|
|
128
|
+
breakpoint as the visible trigger (`useBreakpointUp("desktop")`), or use one
|
|
129
|
+
Sheet per visible trigger.
|
|
130
|
+
|
|
131
|
+
Leave the Sheet header empty except the close control: `title=""` and
|
|
132
|
+
`aria-label="Menu"` (same as `NavigationSide`). Do not put a visible “Menu”
|
|
133
|
+
heading before the close button. Use the `trigger` slot for the `IconButton`.
|
|
134
|
+
|
|
135
|
+
## Trailing utilities
|
|
136
|
+
|
|
137
|
+
Stay a **row** at every width
|
|
138
|
+
([how-to-page-layout.md](how-to-page-layout.md#action-clusters)). Typical
|
|
139
|
+
cluster, left to right: search → language (if needed) → session → compact
|
|
140
|
+
hamburger.
|
|
141
|
+
|
|
142
|
+
- **Search** — marketing/catalog: visible [`Textfield`](../uxelle-components/Textfield.md)
|
|
143
|
+
(`label={false}`, `aria-label="Search"`) in a [`Layout`](../uxelle-components/Layout.md)
|
|
144
|
+
`width="16rem"` `flexShrink={0}`. Dense product: search `IconButton`. Table/query
|
|
145
|
+
search stays on the page, not here.
|
|
146
|
+
- **Session** — logged out: Log in + optional Register (one high-emphasis CTA).
|
|
147
|
+
Logged in: account `IconButton` + `Menu` (profile, settings, log out), optional
|
|
148
|
+
`NotificationBadge`. Do not keep Register when the user is signed in.
|
|
149
|
+
- Do not bury Log in / search in the Sheet.
|
|
150
|
+
|
|
151
|
+
## Secondary bar (`bottomSlot`)
|
|
152
|
+
|
|
153
|
+
`null` unless there is a persistent **L2 module bar** under the primary bar
|
|
154
|
+
(app section switcher). `NavLink`s there use `navigationLevel="Secondary"`. Not
|
|
155
|
+
breadcrumbs, not in-page tabs, not a dump for extra links.
|
|
156
|
+
|
|
157
|
+
## IA depth: what is inside the menu
|
|
158
|
+
|
|
159
|
+
Treat depth of the information architecture as the switch. Compose existing
|
|
160
|
+
pieces; do not invent a mega-menu component.
|
|
161
|
+
|
|
162
|
+
**Open on click, not hover.** Destination panels are `Menu` `pattern="disclosure"`
|
|
163
|
+
with a `NavLink` trigger (no `href`, `trailingIcon`) so Tab walks links.
|
|
164
|
+
`NavLink withMenu` is overflow labeled More only (`pattern="menu"`, Tab
|
|
165
|
+
dismisses). `keyboard_arrow_down` on a category trigger signals a disclosure.
|
|
166
|
+
`chevron_right` on a row means **another column / level**, not outbound.
|
|
167
|
+
|
|
168
|
+
### One level (flat site)
|
|
169
|
+
|
|
170
|
+
Destinations are peers. `centerSlot={null}`. The Sheet holds a `List` of
|
|
171
|
+
interactive `ListItem`s with `href` — **no chevrons**. Example: About, Contact.
|
|
172
|
+
Pass `bottomText=""` so the catalog demo line does not show.
|
|
173
|
+
|
|
174
|
+
### Two levels
|
|
175
|
+
|
|
176
|
+
Top-level groups, each with a flat list of leaves.
|
|
177
|
+
|
|
178
|
+
- **From desktop up:** `centerSlot` is `Menu` `pattern="disclosure"` per group
|
|
179
|
+
(`NavLink` trigger, no `href`, `trailingIcon`). Panel is one column of leaf
|
|
180
|
+
`ListItem`s (`href`, no chevron). Optional short group intro above the list.
|
|
181
|
+
Overflow labeled More uses `NavLink withMenu`.
|
|
182
|
+
- **Below desktop:** hide `centerSlot`. Sheet lists the groups as
|
|
183
|
+
[`Accordion`](../uxelle-components/Accordion.md) (omit `headingLevel` — these
|
|
184
|
+
are not page headings). Each panel is the leaf list. Leaves have no chevron.
|
|
185
|
+
Close the Sheet from each leaf `onClick`.
|
|
186
|
+
|
|
187
|
+
### Three levels
|
|
188
|
+
|
|
189
|
+
Top-level areas → categories → leaves. Chevron means another column / level.
|
|
190
|
+
|
|
191
|
+
- **From desktop up:** `centerSlot` uses `Menu` `pattern="disclosure"` with a
|
|
192
|
+
`NavLink` trigger (no `href`, `trailingIcon`). The panel is a **wide**
|
|
193
|
+
`Layout` row, not a skinny dropdown:
|
|
194
|
+
1. **Intro column** — area title (`Text`), one-line descriptor, `Link` to the
|
|
195
|
+
area landing. Quieter surface; not a list.
|
|
196
|
+
2. **Category column** — interactive `ListItem`s. Rows with children get
|
|
197
|
+
`trailingSlot` + `trailingIconName="chevron_right"`. Hold the selected
|
|
198
|
+
category in state and set `activated` from that state (it drives the next
|
|
199
|
+
column). A row with no children is a leaf (no chevron).
|
|
200
|
+
3. **Leaf column** — map the selected category to `ListItem` `href` rows with
|
|
201
|
+
**no** chevron. Optional “View all {category}” at the bottom of this
|
|
202
|
+
column only.
|
|
203
|
+
- **Below desktop:** do **not** lay out three columns in the Sheet. Same tree as
|
|
204
|
+
a **drill-down**: Accordion (or nested Accordion) so one extra level expands in
|
|
205
|
+
place; leaves are `href` rows. One visible extra level at a time.
|
|
206
|
+
|
|
207
|
+
Selecting a category in the desktop panel **updates the next column in place**
|
|
208
|
+
(does not navigate away). Only leaf rows and “Explore …” / “View all …”
|
|
209
|
+
navigate. Close the panel or Sheet after a leaf navigation (`onOpenChange` /
|
|
210
|
+
`setMenuOpen(false)`).
|
|
211
|
+
|
|
212
|
+
## Responsive
|
|
213
|
+
|
|
214
|
+
- One-level: hamburger may sit in `trailingSlot` at every width (valid on
|
|
215
|
+
desktop and below desktop).
|
|
216
|
+
- Two- and three-level: show `centerSlot` from desktop up; below desktop set
|
|
217
|
+
`centerSlot={null}` and put the menu `IconButton` + Sheet in `trailingSlot`.
|
|
218
|
+
Switch with `useBreakpointUp("desktop")`
|
|
219
|
+
([how-to-page-layout.md](how-to-page-layout.md#responsiveness)). Never
|
|
220
|
+
hard-code breakpoint px. Do not use the tablet band for this chrome switch.
|
|
221
|
+
- Trailing utilities stay a row at every width.
|
|
222
|
+
|
|
223
|
+
## Spacing
|
|
224
|
+
|
|
225
|
+
`Navigation` is **density-invariant** chrome (its own tokens). Do not wrap it in
|
|
226
|
+
extra `p` / `gap`. Lockup lines use `gap={0}` — not the Tight step. Slot content
|
|
227
|
+
inherits the host experience’s density mode ([density.md](density.md)).
|
|
228
|
+
|
|
229
|
+
## A11y
|
|
230
|
+
|
|
231
|
+
[how-to-accessibility.md](how-to-accessibility.md). `Navigation` already exposes
|
|
232
|
+
named `nav` landmarks. Name the home control on the Logo’s wrapping `<a>`
|
|
233
|
+
(`aria-label="Home"`); keep the mark decorative. Name the menu `IconButton`.
|
|
234
|
+
`aria-current="page"` on the current leaf `NavLink` / `ListItem` `href`.
|
|
235
|
+
`activated` on the selected mega-menu category (not a leaf). Name the Sheet with
|
|
236
|
+
`aria-label="Menu"` and `title=""` so the header is only the close control.
|
|
237
|
+
Every `IconButton` has an `aria-label`.
|
|
238
|
+
|
|
239
|
+
## Color
|
|
240
|
+
|
|
241
|
+
`Navigation` owns each band’s surface — style slot content with role tokens; do
|
|
242
|
+
not hardcode color ([how-to-color.md](how-to-color.md)). Secondary-bar
|
|
243
|
+
`NavLink`s use `navigationLevel="Secondary"` so they follow that band.
|
|
244
|
+
|
|
245
|
+
## React
|
|
246
|
+
|
|
247
|
+
Import from `@uxelle/components`. One-level marketing/public default (lockup +
|
|
248
|
+
trailing hamburger Sheet). Control the Sheet with `open` / `onOpenChange` and
|
|
249
|
+
close it from each leaf `onClick`. `desktopUp` is unused here because the menu
|
|
250
|
+
stays in `trailingSlot` at every width.
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
254
|
+
|
|
255
|
+
<Navigation
|
|
256
|
+
bottomSlot={null}
|
|
257
|
+
leadingSlot={
|
|
258
|
+
<>
|
|
259
|
+
<a href="/" aria-label="Home">
|
|
260
|
+
<Logo name="generic" width={48} interactive aria-hidden />
|
|
261
|
+
</a>
|
|
262
|
+
<Layout
|
|
263
|
+
display="flex"
|
|
264
|
+
flexDirection="column"
|
|
265
|
+
gap={0}
|
|
266
|
+
>
|
|
267
|
+
<Text type="Condensed Alt" truncation width={false}>
|
|
268
|
+
Crop Science
|
|
269
|
+
</Text>
|
|
270
|
+
<Text type="Condensed" truncation width={false}>
|
|
271
|
+
United States
|
|
272
|
+
</Text>
|
|
273
|
+
</Layout>
|
|
274
|
+
</>
|
|
275
|
+
}
|
|
276
|
+
centerSlot={null}
|
|
277
|
+
trailingSlot={
|
|
278
|
+
<>
|
|
279
|
+
<LanguageSelector value="EN" />
|
|
280
|
+
<Button emphasis="low" size="small">
|
|
281
|
+
Log in
|
|
282
|
+
</Button>
|
|
283
|
+
<Button emphasis="high" size="small">
|
|
284
|
+
Register
|
|
285
|
+
</Button>
|
|
286
|
+
<Sheet
|
|
287
|
+
open={menuOpen}
|
|
288
|
+
onOpenChange={setMenuOpen}
|
|
289
|
+
direction="Right"
|
|
290
|
+
title=""
|
|
291
|
+
aria-label="Menu"
|
|
292
|
+
trigger={
|
|
293
|
+
<IconButton
|
|
294
|
+
emphasis="low"
|
|
295
|
+
size="small"
|
|
296
|
+
iconName="menu"
|
|
297
|
+
iconVariant="sharpUnfilled"
|
|
298
|
+
aria-label="Menu"
|
|
299
|
+
/>
|
|
300
|
+
}
|
|
301
|
+
>
|
|
302
|
+
<List>
|
|
303
|
+
<ListItem
|
|
304
|
+
interactive
|
|
305
|
+
href="/about"
|
|
306
|
+
centerText="About"
|
|
307
|
+
bottomText=""
|
|
308
|
+
onClick={() => setMenuOpen(false)}
|
|
309
|
+
/>
|
|
310
|
+
<ListItem
|
|
311
|
+
interactive
|
|
312
|
+
href="/contact"
|
|
313
|
+
centerText="Contact"
|
|
314
|
+
bottomText=""
|
|
315
|
+
onClick={() => setMenuOpen(false)}
|
|
316
|
+
/>
|
|
317
|
+
</List>
|
|
318
|
+
</Sheet>
|
|
319
|
+
</>
|
|
320
|
+
}
|
|
321
|
+
/>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Two-level desktop `centerSlot` (hide below desktop; overflow More uses
|
|
325
|
+
`withMenu`; Sheet in trailing carries the same tree):
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
<Menu
|
|
329
|
+
pattern="disclosure"
|
|
330
|
+
direction="Bottom Left"
|
|
331
|
+
trigger={<NavLink label="Crop Protection" trailingIcon />}
|
|
332
|
+
>
|
|
333
|
+
<List>
|
|
334
|
+
<ListItem interactive href="/crop-protection/fungicides" centerText="Fungicides" bottomText="" />
|
|
335
|
+
<ListItem interactive href="/crop-protection/herbicides" centerText="Herbicides" bottomText="" />
|
|
336
|
+
<ListItem interactive href="/crop-protection/trial-data" centerText="Trial Data" bottomText="" />
|
|
337
|
+
</List>
|
|
338
|
+
</Menu>
|
|
339
|
+
<NavLink label="More" withMenu trailingIcon>
|
|
340
|
+
<List>
|
|
341
|
+
<ListItem interactive href="/seeds" centerText="Seeds" bottomText="" />
|
|
342
|
+
</List>
|
|
343
|
+
</NavLink>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Three-level desktop `centerSlot` — selected category in state fills the next
|
|
347
|
+
column; close the panel from each leaf:
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
351
|
+
const [category, setCategory] = useState<"fungicides" | "herbicides">("fungicides");
|
|
352
|
+
|
|
353
|
+
const leaves = {
|
|
354
|
+
fungicides: [
|
|
355
|
+
{ href: "/products/delaro", label: "Delaro" },
|
|
356
|
+
{ href: "/products/luna", label: "Luna Family" },
|
|
357
|
+
{ href: "/crop-protection/fungicides", label: "View all Fungicides" },
|
|
358
|
+
],
|
|
359
|
+
herbicides: [
|
|
360
|
+
{ href: "/products/roundup", label: "Roundup" },
|
|
361
|
+
{ href: "/crop-protection/herbicides", label: "View all Herbicides" },
|
|
362
|
+
],
|
|
363
|
+
} as const;
|
|
364
|
+
|
|
365
|
+
<Menu
|
|
366
|
+
pattern="disclosure"
|
|
367
|
+
direction="Bottom Left"
|
|
368
|
+
open={panelOpen}
|
|
369
|
+
onOpenChange={setPanelOpen}
|
|
370
|
+
trigger={<NavLink label="Crop Protection" trailingIcon />}
|
|
371
|
+
>
|
|
372
|
+
<Layout display="flex" gap="var(--uxl-theme-layout-spacing-small-4)">
|
|
373
|
+
<Layout display="flex" flexDirection="column" gap="var(--uxl-theme-layout-spacing-micro-2)">
|
|
374
|
+
<Text type="Condensed Alt" width={false}>
|
|
375
|
+
Crop Protection
|
|
376
|
+
</Text>
|
|
377
|
+
<Text type="Condensed" width={false}>
|
|
378
|
+
Innovation to help farmers protect their harvests.
|
|
379
|
+
</Text>
|
|
380
|
+
<Link href="/crop-protection">Explore Crop Protection</Link>
|
|
381
|
+
</Layout>
|
|
382
|
+
<List>
|
|
383
|
+
<ListItem
|
|
384
|
+
interactive
|
|
385
|
+
activated={category === "fungicides"}
|
|
386
|
+
trailingSlot
|
|
387
|
+
trailingIconName="chevron_right"
|
|
388
|
+
centerText="Fungicides"
|
|
389
|
+
bottomText=""
|
|
390
|
+
onClick={() => setCategory("fungicides")}
|
|
391
|
+
/>
|
|
392
|
+
<ListItem
|
|
393
|
+
interactive
|
|
394
|
+
activated={category === "herbicides"}
|
|
395
|
+
trailingSlot
|
|
396
|
+
trailingIconName="chevron_right"
|
|
397
|
+
centerText="Herbicides"
|
|
398
|
+
bottomText=""
|
|
399
|
+
onClick={() => setCategory("herbicides")}
|
|
400
|
+
/>
|
|
401
|
+
<ListItem
|
|
402
|
+
interactive
|
|
403
|
+
href="/crop-protection/trial-data"
|
|
404
|
+
centerText="Trial Data"
|
|
405
|
+
bottomText=""
|
|
406
|
+
onClick={() => setPanelOpen(false)}
|
|
407
|
+
/>
|
|
408
|
+
</List>
|
|
409
|
+
<List>
|
|
410
|
+
{leaves[category].map((item) => (
|
|
411
|
+
<ListItem
|
|
412
|
+
key={item.href}
|
|
413
|
+
interactive
|
|
414
|
+
href={item.href}
|
|
415
|
+
centerText={item.label}
|
|
416
|
+
bottomText=""
|
|
417
|
+
onClick={() => setPanelOpen(false)}
|
|
418
|
+
/>
|
|
419
|
+
))}
|
|
420
|
+
</List>
|
|
421
|
+
</Layout>
|
|
422
|
+
</Menu>
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## A2UI
|
|
426
|
+
|
|
427
|
+
No `getUxelleRecipe("navigation")`. Hosts often own chrome; when the surface
|
|
428
|
+
already has `A2uiNavigation`, use the same slot assignment. Runtime chat has no
|
|
429
|
+
`matchMedia` — default to one-level (lockup + trailing `A2uiSheet` `direction`
|
|
430
|
+
`"Right"`). Empty or omitted child lists become catalog placeholders (the adapter
|
|
431
|
+
maps them to `undefined`); prefer host-owned chrome when a region must be absent.
|
|
432
|
+
Compose `A2uiLogo`, `A2uiText`, `A2uiNavLink`, `A2uiList` / `A2uiListItem`,
|
|
433
|
+
`A2uiIconButton`, `A2uiSheet`. See [a2ui.md](a2ui.md).
|
|
@@ -24,6 +24,8 @@ A small set of headline numbers — revenue, active users, uptime, "10k+ teams."
|
|
|
24
24
|
- Do not put `onClick` on StatTile — put a `Link` or `IconButton` in
|
|
25
25
|
`trailingSlotContent`.
|
|
26
26
|
- Do not signal a delta with color alone — pair it with a sign or word.
|
|
27
|
+
- Do not wrap StatTile labels yourself. The label stays on one line and
|
|
28
|
+
ellipsizes; hover or focus a truncated label to read the full name.
|
|
27
29
|
|
|
28
30
|
## Regions
|
|
29
31
|
|
|
@@ -50,10 +52,10 @@ band on a `spacious` page it adds breathing with the **Band** step (`large-15`)
|
|
|
50
52
|
## A11y
|
|
51
53
|
|
|
52
54
|
Each figure and its label read together. StatTile names the group from `label`
|
|
53
|
-
automatically (including `valueFirst`)
|
|
54
|
-
|
|
55
|
-
keep the meaning in text, not
|
|
56
|
-
[how-to-accessibility.md](how-to-accessibility.md).
|
|
55
|
+
automatically (including `valueFirst`), including when the visible label is
|
|
56
|
+
ellipsized. If there is no label, set `aria-label` that combines the figure and
|
|
57
|
+
its meaning. If the trailing slot holds a delta, keep the meaning in text, not
|
|
58
|
+
color alone. See [how-to-accessibility.md](how-to-accessibility.md).
|
|
57
59
|
|
|
58
60
|
## Color
|
|
59
61
|
|