@apliteni/apliteni-ui 0.87.0 → 0.89.0

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.
@@ -0,0 +1,35 @@
1
+ # Button labels
2
+
3
+ ## Keep the words
4
+
5
+ <!-- rule: words-first -->
6
+
7
+ **Rule:** Give every button words, and add a glyph only to help those words.
8
+
9
+ **Do:** Export, Rename and Share, named at rest.
10
+
11
+ **Don't:** The same three, each name waiting on a hover a finger never performs.
12
+
13
+ ## Let the glyph repeat the verb
14
+
15
+ <!-- rule: glyph-repeats-the-verb -->
16
+
17
+ **Rule:** Add a glyph beside a label only when it depicts that label’s action, and treat every button in a row the same way.
18
+
19
+ **Except:** A dismissing action beside a committing one is not a peer, and stays plain.
20
+
21
+ **Do:** The arrow downloads, the sheets copy.
22
+
23
+ **Don't:** A table is what is exported, not the export, and the plain neighbour reads as lesser.
24
+
25
+ ## Earn a wordless button
26
+
27
+ <!-- rule: wordless-earns-it -->
28
+
29
+ **Rule:** Drop a button’s words only when its glyph is learned everywhere, its action is on the closed list, and its name is left to assistive technology and a hover title.
30
+
31
+ **Except:** [Icon-only controls](https://ui.apli.tech/storybook/?path=/story/guidelines-iconography--iconography) is the list, and it grows by decision. The folded rail shows its names beside the glyphs instead, so those carry no title.
32
+
33
+ **Do:** Overflow is on that list; the funnel is not.
34
+
35
+ **Don't:** A funnel could filter, sort or export, and the cog gave up the word that said which.
@@ -8,7 +8,7 @@
8
8
 
9
9
  **Why:** Without a legend, readers need labels for unfamiliar glyphs.
10
10
 
11
- **Except:** The list grows by decision, not review debate. Today: `x` (close or dismiss), `copy` (copy to clipboard), `moreHorizontal` (overflow menu), `moreVertical` (overflow menu), `chevronDown` (expand or collapse), `chevronUp` (expand or collapse), and the shell’s theme toggle, sidebar toggle and collapsed-rail links.
11
+ **Except:** The list grows by decision, not review debate, and [Earn a wordless button](https://ui.apli.tech/storybook/?path=/story/guidelines-button-labels--button-labels) states the three tests an entry passes. Today: `x` (close or dismiss), `copy` (copy to clipboard), `moreHorizontal` (overflow menu), `moreVertical` (overflow menu), `chevronDown` (expand or collapse), `chevronUp` (expand or collapse). The shell’s own chrome — theme toggle, sidebar toggle, collapsed-rail links — is wordless by a separate decision, not by passing those tests.
12
12
 
13
13
  **Do:** Show an overflow menu without words; keep the Settings label.
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.87.0",
3
+ "version": "0.89.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
@@ -57,6 +57,7 @@
57
57
  "build": "npm run build --workspace react",
58
58
  "prepare": "npm run build",
59
59
  "test": "for d in src stories site scripts; do [ -d \"$d\" ] || { echo \"npm test: no such directory: $d\" >&2; exit 1; }; done; node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=./scripts/slow-tests.mjs --test-reporter-destination=stdout 'src/**/*.test.js' 'stories/**/*.test.js' 'site/**/*.test.js' 'scripts/**/*.test.js'",
60
+ "check:words": "node scripts/word-budget.mjs",
60
61
  "tokens:sync": "node scripts/sync-brand-tokens.mjs",
61
62
  "tokens:check": "node scripts/sync-brand-tokens.mjs --check",
62
63
  "tokens:drift": "node scripts/brand-drift.mjs"
package/react/README.md CHANGED
@@ -515,11 +515,12 @@ instead, and the word goes to the track's accessible name. The file reads as a
515
515
  stack: the name on top with the actions at its end, the size — or the failure
516
516
  message — under it, and the track the full width below. No tier takes a second
517
517
  line. The name truncates its stem and keeps its extension, with the whole name in
518
- a `title`, and nothing is hidden or moved at any width. `Remove` is the icon-only
519
- `x`, named by `removeLabel`; `Retry` keeps its word. Both appear only when you
520
- handle them. Children render above the row, inside the region the target covers;
521
- pass `dragging` to drive that target from a parent. `accept` filters the system
522
- picker only — validate type and size yourself.
518
+ a `title`, and nothing is hidden or moved at any width. `Remove` is the kit's
519
+ small text button, worded by `removeLabel`; `Retry` keeps its word beside a
520
+ glyph. Both appear only when you handle them. Children render above the row,
521
+ inside the region the target covers; pass `dragging` to drive that target from
522
+ a parent. `accept` filters the system picker only — validate type and size
523
+ yourself.
523
524
 
524
525
  Use `Field` when an existing labelled control needs the kit frame. Spread its
525
526
  render-prop attributes onto one labelable control, and apply the existing control
@@ -698,7 +698,7 @@ type FileDropProps = {
698
698
  accept?: string;
699
699
  /** The words on the drop target. */
700
700
  dropLabel?: string;
701
- /** Names the icon-only remove button; it carries no visible word. */
701
+ /** The word on the remove button. */
702
702
  removeLabel?: string;
703
703
  retryLabel?: string;
704
704
  /** The file the consumer is holding. Left out, the row is at rest. */
@@ -2871,7 +2871,7 @@ function FileDrop({
2871
2871
  note,
2872
2872
  accept,
2873
2873
  dropLabel = "Drop to upload",
2874
- removeLabel = "Remove file",
2874
+ removeLabel = "Remove",
2875
2875
  retryLabel = "Retry",
2876
2876
  file,
2877
2877
  dragging,
@@ -2928,11 +2928,9 @@ function FileDrop({
2928
2928
  {
2929
2929
  variant: "ghost",
2930
2930
  size: "sm",
2931
- icon: "x",
2932
- iconOnly: true,
2933
- "aria-label": removeLabel,
2934
2931
  onClick: onRemove,
2935
- disabled
2932
+ disabled,
2933
+ children: removeLabel
2936
2934
  }
2937
2935
  )
2938
2936
  ] })
@@ -150,6 +150,24 @@ export const iconNames = Object.keys(ICONS);
150
150
  // `gear` or `externalLink`, because a reader meets them one at a time and a
151
151
  // toolbar is not a legend. Adding an entry is a decision recorded here, which
152
152
  // is the point: the alternative rules read well and could not be gated.
153
+ //
154
+ // Guidelines / Button labels states the three tests an entry passes: the glyph is
155
+ // learned everywhere, its action is on this list, and the control keeps a name
156
+ // assistive technology reads and a hover shows. button({ iconOnly }) writes
157
+ // `aria-label` and `title` together, so a control built from the list below has
158
+ // both — and neither reaches a keyboard reader who is not running one.
159
+ //
160
+ // The shell's folded-rail links and its sidebar toggle are the stated exception:
161
+ // they show the label itself beside the glyph on hover and on focus, so they carry
162
+ // no `title` — stories/apps/shell-rail.test.js holds them to that. They are a
163
+ // separate decision about the shell's own chrome, not entries that passed the
164
+ // tests above.
165
+ //
166
+ // `filter` — Lucide's funnel — fails the first test. Depicting a thing is not the
167
+ // fault; `copy` is on this list and depicts one too. The kit reads the funnel alone
168
+ // as not saying whether the control filters, sorts or exports — its own inference
169
+ // from the sources in docs/specification.md, not a measured recognition finding.
170
+ // See #565.
153
171
  export const iconOnlyAllowed = {
154
172
  x: 'close or dismiss',
155
173
  copy: 'copy to clipboard',
@@ -159,6 +177,21 @@ export const iconOnlyAllowed = {
159
177
  chevronUp: 'expand or collapse',
160
178
  };
161
179
 
180
+ // The word a wordless control's name may open with. The list above says which
181
+ // ACTIONS may drop their text; without this, a glyph on it carries any action a
182
+ // caller likes — `copy` for Duplicate, `x` for Cancel — and the gate stays green,
183
+ // which is what #566's review found. The opening word, not the whole name, so
184
+ // "Copy project ID" is Copy naming what it copies.
185
+ // stories/guidelines/iconography.test.js reads call sites against both maps.
186
+ export const iconOnlyNames = {
187
+ x: ['close', 'dismiss'],
188
+ copy: ['copy'],
189
+ moreHorizontal: ['more'],
190
+ moreVertical: ['more'],
191
+ chevronDown: ['expand', 'collapse', 'show', 'hide'],
192
+ chevronUp: ['expand', 'collapse', 'show', 'hide'],
193
+ };
194
+
162
195
  // What a glyph means when a component picks it for the reader rather than a
163
196
  // caller naming it. Two rules hold the map together:
164
197
  //
package/src/index.d.ts CHANGED
@@ -45,6 +45,7 @@ export declare const SCORE: Record<'exact' | 'prefix' | 'wordStart' | 'contains'
45
45
  export declare const iconNames: string[];
46
46
  export declare const iconCategories: { name: string; names: string[] }[];
47
47
  export declare const iconOnlyAllowed: Record<string, string>;
48
+ export declare const iconOnlyNames: Record<string, string[]>;
48
49
  export declare const iconMeanings: Record<string, string>;
49
50
  export declare const sun: string;
50
51
  export declare const moon: string;