@antadesign/anta 0.1.1-dev.8 → 0.2.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.
Files changed (43) hide show
  1. package/README.md +2 -2
  2. package/dist/components/Button.js +4 -3
  3. package/dist/components/Menu.d.ts +61 -0
  4. package/dist/components/Menu.js +34 -0
  5. package/dist/components/MenuGroup.d.ts +24 -0
  6. package/dist/components/MenuGroup.js +19 -0
  7. package/dist/components/MenuItem.d.ts +49 -0
  8. package/dist/components/MenuItem.js +40 -0
  9. package/dist/components/MenuSeparator.d.ts +14 -0
  10. package/dist/components/MenuSeparator.js +7 -0
  11. package/dist/components/Tag.d.ts +55 -0
  12. package/dist/components/Tag.js +40 -0
  13. package/dist/components/Text.d.ts +4 -2
  14. package/dist/components/Tooltip.d.ts +4 -2
  15. package/dist/elements/a-button.css +31 -8
  16. package/dist/elements/a-icon.shapes.css +4 -0
  17. package/dist/elements/a-icon.shapes.d.ts +2 -1
  18. package/dist/elements/a-icon.shapes.js +3 -1
  19. package/dist/elements/a-menu-group.css +21 -0
  20. package/dist/elements/a-menu-group.d.ts +13 -0
  21. package/dist/elements/a-menu-group.js +15 -0
  22. package/dist/elements/a-menu-item.css +162 -0
  23. package/dist/elements/a-menu-item.d.ts +27 -0
  24. package/dist/elements/a-menu-item.js +29 -0
  25. package/dist/elements/a-menu-separator.css +15 -0
  26. package/dist/elements/a-menu-separator.d.ts +12 -0
  27. package/dist/elements/a-menu-separator.js +15 -0
  28. package/dist/elements/a-menu.css +34 -0
  29. package/dist/elements/a-menu.d.ts +120 -0
  30. package/dist/elements/a-menu.js +649 -0
  31. package/dist/elements/a-tag.css +196 -0
  32. package/dist/elements/a-text.css +17 -13
  33. package/dist/elements/a-tooltip.css +21 -1
  34. package/dist/elements/a-tooltip.d.ts +7 -0
  35. package/dist/elements/a-tooltip.js +96 -19
  36. package/dist/elements/index.d.ts +2 -4
  37. package/dist/elements/index.js +1 -6
  38. package/dist/general_types.d.ts +15 -35
  39. package/dist/index.d.ts +2 -4
  40. package/dist/index.js +2 -4
  41. package/dist/jsx-runtime.d.ts +6 -5
  42. package/dist/reset.css +11 -7
  43. package/package.json +13 -15
@@ -0,0 +1,196 @@
1
+ @layer anta {
2
+ /* `<a-tag>` is intentionally CSS-only — no JS, no shadow DOM, no
3
+ `customElements.define`. It's a small uppercase pill / chip styled
4
+ purely from attributes; the `Tag` JSX wrapper composes its content
5
+ from `icon` / `label` / `value` / `iconTrailing` props (mirroring
6
+ `<a-button>`) into the light-DOM child elements styled below.
7
+
8
+ Sizing is intrinsic — no fixed height. Like `<a-button>`, the pill's
9
+ height falls out of `line-height` + `padding-block` + the border, so
10
+ the text is never clipped and a consumer can retune the padding
11
+ tokens without fighting a hard height.
12
+
13
+ Color comes from Anta's theme-aware semantic tokens (`--text-*`,
14
+ `--bg-*`), so the named tones need no `.dark` rules — the tokens
15
+ re-theme themselves. Only the custom-tone `oklch` knobs are re-tuned
16
+ for dark mode. The hairline border derives from `--tag-text`, so
17
+ every tone (named or custom) gets a matching edge. */
18
+
19
+ a-tag {
20
+ --tag-text: var(--text-3);
21
+ --tag-bg: var(--bg-4);
22
+ --tag-border: color-mix(in oklch, var(--tag-text) 9%, transparent);
23
+ --tag-separator: color-mix(in oklch, var(--tag-text) 30%, transparent);
24
+
25
+ /* 1.5px top/bottom — the extra 0.5px each side compensates for the
26
+ 0.5px (vs 1px) border, keeping the pill at its original 20px height. */
27
+ --tag-padding-block: 1.5px;
28
+ --tag-padding-inline: 6.5px;
29
+ --tag-icon-size: 13px;
30
+ --tag-label-weight: 600;
31
+ --tag-gap: 0.6ch;
32
+
33
+ display: inline-flex;
34
+ align-items: center;
35
+ gap: var(--tag-gap);
36
+ box-sizing: border-box;
37
+ max-width: 100%;
38
+ vertical-align: middle;
39
+ overflow: hidden;
40
+ white-space: nowrap;
41
+ -webkit-user-drag: none;
42
+
43
+ color: var(--tag-text);
44
+ background: var(--tag-bg);
45
+ border: 0.5px solid var(--tag-border);
46
+ border-radius: 22px;
47
+
48
+ padding-block: var(--tag-padding-block);
49
+ padding-inline: var(--tag-padding-inline);
50
+
51
+ font-family: var(--sans-serif);
52
+ font-weight: 450;
53
+ font-size: 11px;
54
+ line-height: 16px;
55
+ text-transform: uppercase;
56
+ letter-spacing: 0.08ch;
57
+ /* Tabular figures are always on (so counts / versions / timers don't
58
+ reflow), plus `ss05` alternate numerals — both layered on top of
59
+ the default ss01 / case / calt set. */
60
+ font-feature-settings: 'tnum' 1, 'ss05' 1, 'ss01', 'case', 'calt';
61
+ }
62
+
63
+ /* Icons scale to the pill (the default 16px a-icon is too big here) and
64
+ don't shrink in the flex row. */
65
+ a-tag a-icon {
66
+ --icon-size: var(--tag-icon-size);
67
+ flex: none;
68
+ }
69
+
70
+ /* label / value segments. Each ellipsizes independently if the pill is
71
+ width-constrained. */
72
+ a-tag a-tag-label,
73
+ a-tag a-tag-value {
74
+ display: inline-block;
75
+ min-width: 0;
76
+ overflow: hidden;
77
+ text-overflow: ellipsis;
78
+ white-space: nowrap;
79
+ }
80
+ /* `value` is the default, unmodified text — it just inherits the tag's
81
+ color and base weight. */
82
+ a-tag a-tag-value { font-weight: 450; }
83
+ /* `label` is the bold "key" placed before the value: same color, just
84
+ heavier (weight 600). It only renders as <a-tag-label> when paired
85
+ with a value — the JSX wrapper drops a lone label into <a-tag-value>
86
+ so a standalone tag keeps the default styling. No divider; the weight
87
+ contrast does the separating. */
88
+ a-tag a-tag-label { font-weight: var(--tag-label-weight); }
89
+
90
+ /* Optical edge padding: an icon at an edge needs less room than a text
91
+ edge, so trim ~2px on whichever side a leading / trailing icon sits.
92
+ `max(0px, …)` keeps it from going negative at small sizes. */
93
+ a-tag:has(> a-icon:first-child) { padding-inline-start: max(0px, var(--tag-padding-inline) - 2px); }
94
+ a-tag:has(> a-icon:last-child) { padding-inline-end: max(0px, var(--tag-padding-inline) - 2px); }
95
+
96
+ /* Segmented children — when raw children (not the structured label /
97
+ value) are passed, each segment after the first gets a hairline
98
+ divider, the legacy multi-part style. Icons and the label / value
99
+ elements are excluded, so `label` + `value` stay divider-less and a
100
+ leading / trailing icon sits flush. The flex `gap` sits to the left
101
+ of the border and `padding-left` balances it on the right. */
102
+ a-tag
103
+ > :not(a-icon, a-tag-label, a-tag-value)
104
+ ~ :not(a-icon, a-tag-label, a-tag-value) {
105
+ padding-left: var(--tag-gap);
106
+ border-left: 0.5px solid var(--tag-separator);
107
+ }
108
+
109
+ /* Named tones — re-point text + bg only; the border re-derives, and
110
+ dark mode falls out of the semantic tokens for free. */
111
+ a-tag[tone="brand"] { --tag-text: var(--text-2-brand); --tag-bg: var(--bg-4-brand); }
112
+ a-tag[tone="info"] { --tag-text: var(--text-2-info); --tag-bg: var(--bg-4-info); }
113
+ a-tag[tone="success"] { --tag-text: var(--text-2-success); --tag-bg: var(--bg-4-success); }
114
+ a-tag[tone="warning"] { --tag-text: var(--text-2-warning); --tag-bg: var(--bg-4-warning); }
115
+ a-tag[tone="critical"] { --tag-text: var(--text-2-critical); --tag-bg: var(--bg-4-critical); }
116
+
117
+ /* Custom tone — any non-named `tone` value. Keep the source hue;
118
+ pin lightness/chroma so any input reads like a sibling of the named
119
+ tones. Knobs are the only numbers to tune; the dark block re-tunes
120
+ them and the formulas resolve against whichever value computes.
121
+ The JSX wrapper writes `--tag-tone-source` inline; the typed
122
+ `attr()` fallback picks up raw `<a-tag tone="…">` on modern engines.
123
+ (oklch relative color: Chrome 119+, Safari 16.4+, Firefox 128+.) */
124
+ a-tag[tone]:not(
125
+ [tone="neutral"],
126
+ [tone="brand"],
127
+ [tone="info"],
128
+ [tone="success"],
129
+ [tone="warning"],
130
+ [tone="critical"]
131
+ ) {
132
+ --tag-tone-source: attr(tone type(<color>), currentColor);
133
+
134
+ --_tag-text-l: 0.46; /* fg lightness */
135
+ --_tag-text-c: 0.17; /* fg chroma */
136
+ --_tag-bg-l: 0.96; /* tinted-bg lightness — a hair above the named --bg-4 so the tint reads light */
137
+ --_tag-bg-c: 0.04; /* tinted-bg chroma — light pastel, like --bg-4-{tone} */
138
+
139
+ --tag-text: oklch(from var(--tag-tone-source) var(--_tag-text-l) var(--_tag-text-c) h);
140
+ --tag-bg: oklch(from var(--tag-tone-source) var(--_tag-bg-l) var(--_tag-bg-c) h);
141
+ }
142
+
143
+ /* Custom tone, dark mode — lighter fg; the tint sits a touch above the
144
+ dark --bg-4-{tone} lightness so it doesn't read pitch-black. */
145
+ .dark a-tag[tone]:not(
146
+ [tone="neutral"],
147
+ [tone="brand"],
148
+ [tone="info"],
149
+ [tone="success"],
150
+ [tone="warning"],
151
+ [tone="critical"]
152
+ ) {
153
+ --_tag-text-l: 0.80;
154
+ --_tag-text-c: 0.11;
155
+ --_tag-bg-l: 0.21;
156
+ --_tag-bg-c: 0.05;
157
+ }
158
+
159
+ a-tag[size="small"] {
160
+ --tag-padding-block: 0.5px;
161
+ --tag-padding-inline: 4.5px;
162
+ --tag-icon-size: 11px;
163
+
164
+ font-size: 10px;
165
+ line-height: 14px;
166
+ }
167
+
168
+ /* Large — 24px tall (line-height 18 + 2×2.5 padding + 2×0.5 border),
169
+ on par with Button's large. */
170
+ a-tag[size="large"] {
171
+ --tag-padding-block: 2.5px;
172
+ --tag-padding-inline: 8.5px;
173
+ --tag-icon-size: 15px;
174
+
175
+ font-size: 12px;
176
+ line-height: 18px;
177
+ }
178
+
179
+ /* Normal-case opt-out: drop the uppercase transform for proportional,
180
+ mixed-case text. Keep a small body-text tracking (Anta's 0.02ch)
181
+ rather than zero — uppercase just needs more (0.08ch). Tabular
182
+ figures + `ss05` stay on (only the case / stylistic-set extras
183
+ change). */
184
+ a-tag[nocaps] {
185
+ font-size: 12px;
186
+ text-transform: none;
187
+ letter-spacing: 0.02ch;
188
+ font-feature-settings: 'tnum' 1, 'ss05' 1, 'lnum', 'ss01', 'ss04', 'case', 'calt';
189
+ }
190
+ /* Large + nocaps reads larger too — bump to 13px (one over the 12px
191
+ uppercase large), keeping the same +1px over-uppercase relationship
192
+ medium has. Height is unchanged (driven by line-height + padding). */
193
+ a-tag[size="large"][nocaps] {
194
+ font-size: 13px;
195
+ }
196
+ }
@@ -1,6 +1,8 @@
1
1
  @layer anta {
2
2
  a-text {
3
- --text-color: var(--text-1);
3
+ /* Default (no `priority`) is the SECONDARY level — body text reads at
4
+ --text-2; `priority="primary"` opts into the strongest --text-1. */
5
+ --text-color: var(--text-2);
4
6
  --text-link-color: var(--link-color);
5
7
  --text-link-hover: var(--link-color-hover);
6
8
 
@@ -27,10 +29,12 @@
27
29
  min-width: 0;
28
30
  }
29
31
 
30
- /* Neutral priority overrides. Levels 1 & 2 keep the brand link color;
32
+ /* Neutral priority overrides. The default (no `priority`) is the
33
+ secondary level (--text-2, on the base rule above); `primary` opts
34
+ into the strongest --text-1. Levels 1 & 2 keep the brand link color;
31
35
  levels 3-5 mute the link to currentColor and step the hover up by one
32
36
  level (3→2, 4→3, 5→4). */
33
- a-text[priority="secondary"] { --text-color: var(--text-2); }
37
+ a-text[priority="primary"] { --text-color: var(--text-1); }
34
38
  a-text[priority="tertiary"] { --text-color: var(--text-3); --text-link-color: currentColor; --text-link-hover: var(--text-2); }
35
39
  a-text[priority="quaternary"] { --text-color: var(--text-4); --text-link-color: currentColor; --text-link-hover: var(--text-3); }
36
40
  a-text[priority="quinary"] { --text-color: var(--text-5); --text-link-color: currentColor; --text-link-hover: var(--text-4); }
@@ -39,32 +43,32 @@
39
43
  muted (currentColor) and hover steps up one level of the same tint.
40
44
  Level 1 has no level above; hover keeps the color, only the underline
41
45
  alpha changes. */
42
- a-text[tone="brand"] { --text-color: var(--text-1-brand); --text-link-color: currentColor; --text-link-hover: var(--text-1-brand); }
43
- a-text[tone="brand"][priority="secondary"] { --text-color: var(--text-2-brand); --text-link-hover: var(--text-1-brand); }
46
+ a-text[tone="brand"] { --text-color: var(--text-2-brand); --text-link-color: currentColor; --text-link-hover: var(--text-1-brand); }
47
+ a-text[tone="brand"][priority="primary"] { --text-color: var(--text-1-brand); }
44
48
  a-text[tone="brand"][priority="tertiary"] { --text-color: var(--text-3-brand); --text-link-hover: var(--text-2-brand); }
45
49
  a-text[tone="brand"][priority="quaternary"] { --text-color: var(--text-4-brand); --text-link-hover: var(--text-3-brand); }
46
50
  a-text[tone="brand"][priority="quinary"] { --text-color: var(--text-5-brand); --text-link-hover: var(--text-4-brand); }
47
51
 
48
- a-text[tone="success"] { --text-color: var(--text-1-success); --text-link-color: currentColor; --text-link-hover: var(--text-1-success); }
49
- a-text[tone="success"][priority="secondary"] { --text-color: var(--text-2-success); --text-link-hover: var(--text-1-success); }
52
+ a-text[tone="success"] { --text-color: var(--text-2-success); --text-link-color: currentColor; --text-link-hover: var(--text-1-success); }
53
+ a-text[tone="success"][priority="primary"] { --text-color: var(--text-1-success); }
50
54
  a-text[tone="success"][priority="tertiary"] { --text-color: var(--text-3-success); --text-link-hover: var(--text-2-success); }
51
55
  a-text[tone="success"][priority="quaternary"] { --text-color: var(--text-4-success); --text-link-hover: var(--text-3-success); }
52
56
  a-text[tone="success"][priority="quinary"] { --text-color: var(--text-5-success); --text-link-hover: var(--text-4-success); }
53
57
 
54
- a-text[tone="critical"] { --text-color: var(--text-1-critical); --text-link-color: currentColor; --text-link-hover: var(--text-1-critical); }
55
- a-text[tone="critical"][priority="secondary"] { --text-color: var(--text-2-critical); --text-link-hover: var(--text-1-critical); }
58
+ a-text[tone="critical"] { --text-color: var(--text-2-critical); --text-link-color: currentColor; --text-link-hover: var(--text-1-critical); }
59
+ a-text[tone="critical"][priority="primary"] { --text-color: var(--text-1-critical); }
56
60
  a-text[tone="critical"][priority="tertiary"] { --text-color: var(--text-3-critical); --text-link-hover: var(--text-2-critical); }
57
61
  a-text[tone="critical"][priority="quaternary"] { --text-color: var(--text-4-critical); --text-link-hover: var(--text-3-critical); }
58
62
  a-text[tone="critical"][priority="quinary"] { --text-color: var(--text-5-critical); --text-link-hover: var(--text-4-critical); }
59
63
 
60
- a-text[tone="warning"] { --text-color: var(--text-1-warning); --text-link-color: currentColor; --text-link-hover: var(--text-1-warning); }
61
- a-text[tone="warning"][priority="secondary"] { --text-color: var(--text-2-warning); --text-link-hover: var(--text-1-warning); }
64
+ a-text[tone="warning"] { --text-color: var(--text-2-warning); --text-link-color: currentColor; --text-link-hover: var(--text-1-warning); }
65
+ a-text[tone="warning"][priority="primary"] { --text-color: var(--text-1-warning); }
62
66
  a-text[tone="warning"][priority="tertiary"] { --text-color: var(--text-3-warning); --text-link-hover: var(--text-2-warning); }
63
67
  a-text[tone="warning"][priority="quaternary"] { --text-color: var(--text-4-warning); --text-link-hover: var(--text-3-warning); }
64
68
  a-text[tone="warning"][priority="quinary"] { --text-color: var(--text-5-warning); --text-link-hover: var(--text-4-warning); }
65
69
 
66
- a-text[tone="info"] { --text-color: var(--text-1-info); --text-link-color: currentColor; --text-link-hover: var(--text-1-info); }
67
- a-text[tone="info"][priority="secondary"] { --text-color: var(--text-2-info); --text-link-hover: var(--text-1-info); }
70
+ a-text[tone="info"] { --text-color: var(--text-2-info); --text-link-color: currentColor; --text-link-hover: var(--text-1-info); }
71
+ a-text[tone="info"][priority="primary"] { --text-color: var(--text-1-info); }
68
72
  a-text[tone="info"][priority="tertiary"] { --text-color: var(--text-3-info); --text-link-hover: var(--text-2-info); }
69
73
  a-text[tone="info"][priority="quaternary"] { --text-color: var(--text-4-info); --text-link-hover: var(--text-3-info); }
70
74
  a-text[tone="info"][priority="quinary"] { --text-color: var(--text-5-info); --text-link-hover: var(--text-4-info); }
@@ -8,6 +8,20 @@
8
8
  display: none;
9
9
  }
10
10
 
11
+ /* On touch devices, an anchor that owns a tooltip suppresses the long-press
12
+ text-selection and the iOS callout, so press-and-hold cleanly reveals the
13
+ bubble (see the long-press handling in a-tooltip.ts) instead of selecting
14
+ text or popping the system menu. Scoped to coarse/no-hover pointers, so
15
+ fine-pointer devices keep normal text selection. The anchor is always
16
+ <a-tooltip>'s direct parent (set in connectedCallback), so `:has(> …)`
17
+ targets exactly it; `:where()` keeps specificity at 0 for easy override. */
18
+ @media (hover: none) and (pointer: coarse) {
19
+ :where(:has(> a-tooltip)) {
20
+ -webkit-touch-callout: none;
21
+ user-select: none;
22
+ }
23
+ }
24
+
11
25
  /* Tooltip exposes only the box "chrome" that lives inside the shadow
12
26
  popover and is therefore impossible to reach with plain consumer CSS.
13
27
  The bubble's content is slotted light DOM, so its font / colour / size
@@ -28,6 +42,12 @@
28
42
  0 0 1px color-mix(in oklch, var(--text-1) 50%, transparent);
29
43
  --tooltip-radius: 3px;
30
44
  --tooltip-max-width: min(calc(100vw - 20px), 80ch);
45
+ /* No real border by default — the hairline edge comes from --tooltip-shadow.
46
+ Set e.g. `1px solid var(--border-5)` for an actual border. */
47
+ --tooltip-border: none;
48
+ /* The frost. Set to `none` (with --tooltip-bg: transparent) for a fully
49
+ see-through bubble. */
50
+ --tooltip-backdrop-filter: blur(8px);
31
51
  }
32
52
 
33
53
  /* Dark: a slightly more transparent black frost. A solid black cast shadow
@@ -37,6 +57,6 @@
37
57
  --tooltip-bg: color-mix(in oklch, var(--bg-1) 70%, transparent);
38
58
  --tooltip-shadow:
39
59
  0 2px 16px color-mix(in oklch, black 70%, transparent),
40
- inset 0 0 0 1px color-mix(in oklch, white 25%, transparent);
60
+ inset 0 0 0 1px color-mix(in oklch, white 15%, transparent);
41
61
  }
42
62
  }
@@ -7,6 +7,10 @@ export declare class ATooltipElement extends HTMLElementBase {
7
7
  private anchor;
8
8
  listening: boolean;
9
9
  private shown;
10
+ /** True while the current open was triggered by a touch long-press. Such a
11
+ * bubble is pinned (never cursor-follows) and biases above the anchor so the
12
+ * fingertip doesn't cover it. */
13
+ private touchOpen;
10
14
  private lastMouse?;
11
15
  private debouncedShow?;
12
16
  private teardown?;
@@ -35,6 +39,9 @@ export declare class ATooltipElement extends HTMLElementBase {
35
39
  /** Pinned under the anchor (no cursor-following). Interactive implies this —
36
40
  * you can't move into a bubble that's chasing the cursor. */
37
41
  private get isStatic();
42
+ /** Pinned to the anchor for any reason: the `static`/`interactive` attrs, or
43
+ * a touch long-press (a finger can't track a following bubble). */
44
+ private get isPinned();
38
45
  private get prefersTop();
39
46
  private get delay();
40
47
  private positionToTarget;
@@ -8,6 +8,9 @@ const DEFAULT_DELAY = 250;
8
8
  const FADE_MS = 150;
9
9
  const GRACE_MS = 300;
10
10
  const CLOSE_DELAY = 120;
11
+ const ENTER_TOUCH_DELAY = 500;
12
+ const LEAVE_TOUCH_DELAY = 1500;
13
+ const TOUCH_SLOP = 10;
11
14
  let currentOpen = null;
12
15
  let graceTimer;
13
16
  function graceActive() {
@@ -58,6 +61,10 @@ class ATooltipElement extends HTMLElementBase {
58
61
  anchor = null;
59
62
  listening = false;
60
63
  shown = false;
64
+ /** True while the current open was triggered by a touch long-press. Such a
65
+ * bubble is pinned (never cursor-follows) and biases above the anchor so the
66
+ * fingertip doesn't cover it. */
67
+ touchOpen = false;
61
68
  lastMouse;
62
69
  debouncedShow;
63
70
  teardown;
@@ -88,17 +95,34 @@ class ATooltipElement extends HTMLElementBase {
88
95
  text-align: left;
89
96
 
90
97
  background: var(--tooltip-bg, Canvas);
91
- color: var(--text-1, CanvasText);
98
+ color: var(--text-3, CanvasText);
92
99
  box-shadow: var(--tooltip-shadow, 0 1px 8px rgba(0, 0, 0, 0.2));
93
- backdrop-filter: blur(8px);
100
+ -webkit-backdrop-filter: var(--tooltip-backdrop-filter, blur(8px));
101
+ backdrop-filter: var(--tooltip-backdrop-filter, blur(8px));
94
102
  padding: 5px 8px;
95
- border: none;
103
+ border: var(--tooltip-border, none);
96
104
  border-radius: var(--tooltip-radius, 3px);
97
105
  outline: none;
98
106
 
107
+ /* The bubble establishes its own text baseline so inheritable text
108
+ properties from the anchor (e.g. a Button's condensed "wdth" 88
109
+ axis, its 0.05ch letter-spacing, an uppercase transform, a custom
110
+ font) don't bleed into the slotted content. The content is slotted
111
+ light DOM, so it inherits these *from this container* \u2014 making this
112
+ the single choke point. Values mirror Anta's body text. Consumers
113
+ still customise a single tooltip by styling their own content
114
+ element directly (a class on the content overrides what it inherits
115
+ \u2014 see the Tooltip docs). */
116
+ font-family: var(--sans-serif, system-ui, sans-serif);
99
117
  font-size: 14px;
100
- line-height: 130%;
101
118
  font-weight: 400;
119
+ font-style: normal;
120
+ font-stretch: normal;
121
+ font-variation-settings: "wdth" 100, "slnt" 0, "ital" 0;
122
+ line-height: 1.5;
123
+ letter-spacing: 0.02ch;
124
+ word-spacing: normal;
125
+ text-transform: none;
102
126
  white-space: break-spaces;
103
127
  word-break: break-word;
104
128
  overflow-wrap: break-word;
@@ -196,6 +220,11 @@ class ATooltipElement extends HTMLElementBase {
196
220
  get isStatic() {
197
221
  return this.hasAttribute("static") || this.isInteractive;
198
222
  }
223
+ /** Pinned to the anchor for any reason: the `static`/`interactive` attrs, or
224
+ * a touch long-press (a finger can't track a following bubble). */
225
+ get isPinned() {
226
+ return this.isStatic || this.touchOpen;
227
+ }
199
228
  get prefersTop() {
200
229
  return this.getAttribute("placement") === "top";
201
230
  }
@@ -218,7 +247,7 @@ class ATooltipElement extends HTMLElementBase {
218
247
  if (left + box.width > vw) left = vw - box.width - MARGIN;
219
248
  left = Math.max(MARGIN, left);
220
249
  let top;
221
- if (this.prefersTop) {
250
+ if (this.prefersTop || this.touchOpen) {
222
251
  top = a.top - box.height - MARGIN;
223
252
  if (top < MARGIN) top = a.bottom + MARGIN;
224
253
  } else {
@@ -252,7 +281,7 @@ class ATooltipElement extends HTMLElementBase {
252
281
  });
253
282
  };
254
283
  position(e) {
255
- if (!this.isStatic && e) this.positionToMouse(e);
284
+ if (!this.isPinned && e) this.positionToMouse(e);
256
285
  else this.positionToTarget();
257
286
  }
258
287
  // --- show / hide ---
@@ -284,6 +313,7 @@ class ATooltipElement extends HTMLElementBase {
284
313
  this.cancelHide();
285
314
  if (!this.shown) return;
286
315
  this.shown = false;
316
+ this.touchOpen = false;
287
317
  this.container.hidePopover();
288
318
  this.view.removeEventListener("scroll", this.hide, { capture: true });
289
319
  this.doc.removeEventListener("keydown", this.onKeyDown, true);
@@ -303,7 +333,7 @@ class ATooltipElement extends HTMLElementBase {
303
333
  * the close delay). Cursor-following only — static/interactive bubbles
304
334
  * stay pinned. */
305
335
  onDocMove = (e) => {
306
- if ((this.shown || this.fading) && !this.isStatic) {
336
+ if ((this.shown || this.fading) && !this.isPinned) {
307
337
  this.lastMouse = e;
308
338
  this.positionToMouse(e);
309
339
  }
@@ -311,12 +341,12 @@ class ATooltipElement extends HTMLElementBase {
311
341
  /** Hide after CLOSE_DELAY unless something cancels it first (re-enter, or
312
342
  * another tooltip claiming the slot). Lets the bubble bridge the gap to a
313
343
  * neighbouring anchor — and survive the trip into an interactive bubble. */
314
- scheduleHide = () => {
344
+ scheduleHide = (delay = CLOSE_DELAY) => {
315
345
  this.cancelHide();
316
346
  this.closeTimer = setTimeout(() => {
317
347
  this.closeTimer = void 0;
318
348
  this.hide();
319
- }, CLOSE_DELAY);
349
+ }, delay);
320
350
  };
321
351
  cancelHide() {
322
352
  if (this.closeTimer !== void 0) {
@@ -347,46 +377,93 @@ class ATooltipElement extends HTMLElementBase {
347
377
  const view = this.view;
348
378
  const doc = this.doc;
349
379
  this.makeDebouncedShow();
350
- const onMouseMove = (e) => {
380
+ const onPointerMove = (e) => {
381
+ if (e.pointerType !== "mouse") return;
351
382
  this.lastMouse = e;
352
383
  if (this.shown) {
353
- if (!this.isStatic) this.positionToMouse(e);
384
+ if (!this.isPinned) this.positionToMouse(e);
354
385
  } else {
355
386
  this.trigger(e);
356
387
  }
357
388
  };
358
389
  const onLeave = () => {
359
390
  this.debouncedShow?.cancel();
360
- anchor.removeEventListener("mousemove", onMouseMove);
361
- anchor.removeEventListener("mouseleave", onLeave);
391
+ anchor.removeEventListener("pointermove", onPointerMove);
392
+ anchor.removeEventListener("pointerleave", onLeave);
362
393
  anchor.removeEventListener("blur", onLeave);
363
394
  view.removeEventListener("pagehide", onLeave);
364
395
  doc.removeEventListener("visibilitychange", onLeave);
365
396
  this.scheduleHide();
366
397
  };
367
398
  const onEnter = (e) => {
399
+ if (e.pointerType !== "mouse") return;
368
400
  this.lastMouse = e;
369
- anchor.addEventListener("mousemove", onMouseMove);
370
- anchor.addEventListener("mouseleave", onLeave);
401
+ anchor.addEventListener("pointermove", onPointerMove);
402
+ anchor.addEventListener("pointerleave", onLeave);
371
403
  view.addEventListener("pagehide", onLeave);
372
404
  doc.addEventListener("visibilitychange", onLeave);
373
405
  this.trigger(e);
374
406
  };
375
407
  const onFocus = () => {
408
+ if (!anchor.matches(":focus-visible")) return;
376
409
  anchor.addEventListener("blur", onLeave);
377
410
  view.addEventListener("pagehide", onLeave);
378
411
  doc.addEventListener("visibilitychange", onLeave);
379
412
  this.trigger();
380
413
  };
381
- anchor.addEventListener("mouseenter", onEnter);
414
+ let pressTimer;
415
+ let pressStart;
416
+ const preventContext = (e) => e.preventDefault();
417
+ const clearPressTimer = () => {
418
+ if (pressTimer !== void 0) {
419
+ clearTimeout(pressTimer);
420
+ pressTimer = void 0;
421
+ }
422
+ pressStart = void 0;
423
+ };
424
+ const onPressMove = (e) => {
425
+ if (pressStart && Math.hypot(e.clientX - pressStart.x, e.clientY - pressStart.y) > TOUCH_SLOP) {
426
+ clearPressTimer();
427
+ }
428
+ };
429
+ const onPressEnd = () => {
430
+ clearPressTimer();
431
+ detachPress();
432
+ if (this.shown && this.touchOpen) this.scheduleHide(LEAVE_TOUCH_DELAY);
433
+ };
434
+ const detachPress = () => {
435
+ anchor.removeEventListener("pointermove", onPressMove);
436
+ anchor.removeEventListener("pointerup", onPressEnd);
437
+ anchor.removeEventListener("pointercancel", onPressEnd);
438
+ anchor.removeEventListener("contextmenu", preventContext);
439
+ };
440
+ const onPointerDown = (e) => {
441
+ if (e.pointerType === "mouse") return;
442
+ clearPressTimer();
443
+ pressStart = { x: e.clientX, y: e.clientY };
444
+ anchor.addEventListener("pointermove", onPressMove);
445
+ anchor.addEventListener("pointerup", onPressEnd);
446
+ anchor.addEventListener("pointercancel", onPressEnd);
447
+ anchor.addEventListener("contextmenu", preventContext);
448
+ pressTimer = setTimeout(() => {
449
+ pressTimer = void 0;
450
+ this.touchOpen = true;
451
+ this.show();
452
+ }, ENTER_TOUCH_DELAY);
453
+ };
454
+ anchor.addEventListener("pointerenter", onEnter);
382
455
  anchor.addEventListener("focus", onFocus);
456
+ anchor.addEventListener("pointerdown", onPointerDown);
383
457
  this.teardown = () => {
384
458
  this.debouncedShow?.cancel();
385
- anchor.removeEventListener("mousemove", onMouseMove);
386
- anchor.removeEventListener("mouseenter", onEnter);
387
- anchor.removeEventListener("mouseleave", onLeave);
459
+ clearPressTimer();
460
+ detachPress();
461
+ anchor.removeEventListener("pointermove", onPointerMove);
462
+ anchor.removeEventListener("pointerenter", onEnter);
463
+ anchor.removeEventListener("pointerleave", onLeave);
388
464
  anchor.removeEventListener("focus", onFocus);
389
465
  anchor.removeEventListener("blur", onLeave);
466
+ anchor.removeEventListener("pointerdown", onPointerDown);
390
467
  view.removeEventListener("pagehide", onLeave);
391
468
  doc.removeEventListener("visibilitychange", onLeave);
392
469
  this.listening = false;
@@ -8,8 +8,7 @@
8
8
  *
9
9
  * For a SMALLER footprint, import just the element(s) you use instead:
10
10
  * import '@antadesign/anta/elements/a-tooltip' // registers a-tooltip + its CSS, nothing else
11
- * That granular path pulls in only that element's code (e.g. it won't drag in
12
- * `lottie-web`, which only `a-sticker-animated` needs).
11
+ * That granular path pulls in only that element's code and CSS, nothing else.
13
12
  *
14
13
  * Must only be imported client-side — registration is guarded against missing
15
14
  * `customElements` (SSR), but there's no reason to load it server-side.
@@ -18,7 +17,6 @@ export { AProgressElement, register_a_progress } from './a-progress';
18
17
  export { ATextElement, register_a_text } from './a-text';
19
18
  export { AIconElement, register_a_icon } from './a-icon';
20
19
  export { AButtonElement, register_a_button } from './a-button';
21
- export { AStickerElement, register_a_sticker } from './a-sticker';
22
- export { AStickerAnimatedElement, register_a_sticker_animated } from './a-sticker-animated';
23
20
  export { ATooltipElement, register_a_tooltip } from './a-tooltip';
24
21
  import './a-title.css';
22
+ import './a-tag.css';
@@ -2,23 +2,18 @@ import { AProgressElement, register_a_progress } from "./a-progress";
2
2
  import { ATextElement, register_a_text } from "./a-text";
3
3
  import { AIconElement, register_a_icon } from "./a-icon";
4
4
  import { AButtonElement, register_a_button } from "./a-button";
5
- import { AStickerElement, register_a_sticker } from "./a-sticker";
6
- import { AStickerAnimatedElement, register_a_sticker_animated } from "./a-sticker-animated";
7
5
  import { ATooltipElement, register_a_tooltip } from "./a-tooltip";
8
6
  import "./a-title.css";
7
+ import "./a-tag.css";
9
8
  export {
10
9
  AButtonElement,
11
10
  AIconElement,
12
11
  AProgressElement,
13
- AStickerAnimatedElement,
14
- AStickerElement,
15
12
  ATextElement,
16
13
  ATooltipElement,
17
14
  register_a_button,
18
15
  register_a_icon,
19
16
  register_a_progress,
20
- register_a_sticker,
21
- register_a_sticker_animated,
22
17
  register_a_text,
23
18
  register_a_tooltip
24
19
  };
@@ -108,43 +108,23 @@ export interface ATitleAttributes extends BaseAttributes {
108
108
  'aria-level'?: number | string;
109
109
  }
110
110
  /**
111
- * Attributes for the `<a-sticker>` static sticker carrier.
111
+ * Attributes for the `<a-tag>` styled tag.
112
112
  *
113
- * The SVG payload is passed as the `svg` attribute a markup string.
114
- * On change, the element writes it into its shadow DOM (no light-DOM
115
- * `<slot>`, so children aren't rendered). The JSX wrapper
116
- * (`Sticker{Name}`) sets it from the per-sticker module's inlined SVG.
117
- * Sizing comes from `--sticker-size`.
113
+ * `<a-tag>` has no JS it's a CSS-only styled element. Low-level
114
+ * attributes; for the JSX wrapper use `Tag` from `@antadesign/anta`.
118
115
  */
119
- export interface AStickerAttributes extends BaseAttributes {
120
- /** SVG markup string. On change the element drops it into its shadow
121
- * DOM (`innerHTML`). */
122
- svg?: string;
123
- role?: string;
124
- 'aria-label'?: string;
125
- 'aria-hidden'?: 'true' | 'false' | boolean;
126
- }
127
- /**
128
- * Attributes for the `<a-sticker-animated>` Lottie sticker carrier.
129
- *
130
- * The Lottie payload is passed as the `animation` attribute — a JSON
131
- * string. On change the element `JSON.parse`s it once and drives a
132
- * `lottie-web` player (SVG renderer) inside its shadow DOM. The JSX
133
- * wrapper (`Sticker{Name}Animated`) sets it from the per-sticker
134
- * module's inlined JSON. Sizing comes from the `--sticker-size` CSS
135
- * variable; `paused` controls playback.
136
- */
137
- export interface AStickerAnimatedAttributes extends BaseAttributes {
138
- /** Lottie payload as a JSON string. The element parses it once on
139
- * change. */
140
- animation?: string;
141
- /** Present (any string value, including `""`) freezes the animation.
142
- * A numeric string is parsed as seconds and the player seeks to
143
- * that time before pausing. Omit to play. */
144
- paused?: string | boolean | number;
145
- role?: string;
146
- 'aria-label'?: string;
147
- 'aria-hidden'?: 'true' | 'false' | boolean;
116
+ export interface ATagAttributes extends BaseAttributes {
117
+ /** Semantic tone, or any literal CSS color for a one-off custom tone.
118
+ * Named tones map to the `--text-2-{tone}` / `--bg-4-{tone}` palette;
119
+ * a custom color keeps its hue with lightness/chroma pinned.
120
+ * `'neutral'` is the default gray (same as omitting it). */
121
+ tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
122
+ /** Size variant. `small` = 16px tall, `medium` (default) = 20px,
123
+ * `large` = 24px. */
124
+ size?: 'small' | 'medium' | 'large';
125
+ /** Render in normal case instead of the default uppercase.
126
+ * Presence-based (`''` on, omit off). */
127
+ nocaps?: boolean | '';
148
128
  }
149
129
  /**
150
130
  * Attributes for the `<a-icon>` custom element. `shape` is typed as