@realmorg/ui 2.0.0-alpha.2 → 2.0.0-alpha.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.
Files changed (65) hide show
  1. package/README.md +6 -6
  2. package/components/accordion.html +40 -1
  3. package/components/alert-dialog.html +40 -0
  4. package/components/alert.html +14 -0
  5. package/components/aspect-ratio.html +6 -0
  6. package/components/attachment.html +14 -0
  7. package/components/avatar.html +22 -0
  8. package/components/badge.html +6 -0
  9. package/components/breadcrumb.html +30 -0
  10. package/components/bubble.html +10 -0
  11. package/components/button-group.html +14 -0
  12. package/components/button.html +41 -22
  13. package/components/calendar.html +6 -0
  14. package/components/card.html +30 -0
  15. package/components/carousel.html +22 -0
  16. package/components/chart.html +6 -0
  17. package/components/checkbox.html +6 -0
  18. package/components/code-block.html +6 -0
  19. package/components/collapsible.html +14 -0
  20. package/components/combobox.html +26 -0
  21. package/components/command.html +12 -0
  22. package/components/context-menu.html +14 -0
  23. package/components/dialog.html +49 -3
  24. package/components/direction.html +6 -0
  25. package/components/drawer.html +46 -3
  26. package/components/dropdown-menu.html +75 -1
  27. package/components/empty.html +26 -0
  28. package/components/field.html +26 -0
  29. package/components/hover-card.html +41 -1
  30. package/components/input-group.html +16 -0
  31. package/components/input-otp.html +12 -0
  32. package/components/input.html +12 -0
  33. package/components/item.html +38 -0
  34. package/components/kbd.html +10 -0
  35. package/components/label.html +6 -0
  36. package/components/marker.html +6 -0
  37. package/components/menubar.html +36 -1
  38. package/components/message-scroller.html +6 -0
  39. package/components/message.html +18 -0
  40. package/components/navigation-menu.html +29 -0
  41. package/components/pagination.html +6 -0
  42. package/components/popover.html +53 -1
  43. package/components/progress.html +6 -0
  44. package/components/radio-group.html +6 -0
  45. package/components/resizable.html +14 -0
  46. package/components/scroll-area.html +6 -0
  47. package/components/select.html +12 -0
  48. package/components/separator.html +6 -0
  49. package/components/sheet.html +30 -0
  50. package/components/sidebar.html +52 -2
  51. package/components/skeleton.html +6 -0
  52. package/components/slider.html +28 -1
  53. package/components/spinner.html +6 -0
  54. package/components/switch.html +6 -0
  55. package/components/table.html +6 -0
  56. package/components/tabs.html +18 -0
  57. package/components/textarea.html +12 -0
  58. package/components/toaster.html +6 -0
  59. package/components/toggle-group.html +6 -0
  60. package/components/toggle.html +6 -0
  61. package/components/tooltip.html +43 -1
  62. package/dist/theme.css +13 -0
  63. package/dist/ui.html +1171 -37
  64. package/package.json +4 -3
  65. package/theme.css +13 -0
package/README.md CHANGED
@@ -5,10 +5,10 @@ About 60 accessible components built with [REALM](https://realm.js.org). Each on
5
5
  Load REALM, the theme and the components from the CDN. There is no build step.
6
6
 
7
7
  ```html
8
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
9
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
10
- <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
11
- <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/ui.html"></import-element>
8
+ <link rel="stylesheet" href="https://realm.js.org/dist/realm.css">
9
+ <link rel="stylesheet" href="https://realm.js.org/dist/ui/theme.css">
10
+ <script type="module" src="https://realm.js.org/dist/realm.js"></script>
11
+ <import-element from="https://realm.js.org/dist/ui/ui.html"></import-element>
12
12
 
13
13
  <ui-card>
14
14
  <ui-card-header>
@@ -28,8 +28,8 @@ Load REALM, the theme and the components from the CDN. There is no build step.
28
28
  To load only what you use, import single files instead of `ui.html`:
29
29
 
30
30
  ```html
31
- <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
32
- <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
31
+ <import-element from="https://realm.js.org/dist/ui/components/button.html"></import-element>
32
+ <import-element from="https://realm.js.org/dist/ui/components/card.html"></import-element>
33
33
  ```
34
34
 
35
35
  Each file imports its own dependencies (`sheet.html` loads `dialog.html`), and each file is fetched once.
@@ -14,6 +14,12 @@
14
14
  </script>
15
15
 
16
16
  <style>
17
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
18
+ touch-action does not reach shadow content in iOS Safari. */
19
+ :host,
20
+ * {
21
+ touch-action: manipulation;
22
+ }
17
23
  :host {
18
24
  display: block;
19
25
  }
@@ -41,6 +47,10 @@
41
47
  </script>
42
48
 
43
49
  <style>
50
+ :host,
51
+ * {
52
+ touch-action: manipulation;
53
+ }
44
54
  :host {
45
55
  display: block;
46
56
  border-bottom: 1px solid var(--border);
@@ -79,11 +89,32 @@
79
89
  width: calc(var(--spacing) * 4);
80
90
  height: calc(var(--spacing) * 4);
81
91
  color: var(--muted-foreground);
82
- transition: rotate 0.2s;
92
+ transition: rotate var(--default-transition-duration) var(--default-transition-timing-function);
83
93
  }
84
94
  details[open] svg {
85
95
  rotate: 180deg;
86
96
  }
97
+ details::details-content {
98
+ display: grid;
99
+ grid-template-rows: 0fr;
100
+ transition:
101
+ grid-template-rows var(--default-transition-duration) var(--default-transition-timing-function),
102
+ content-visibility var(--default-transition-duration) allow-discrete;
103
+ }
104
+ details[open]::details-content {
105
+ grid-template-rows: 1fr;
106
+ }
107
+ slot:not([name]) {
108
+ display: block;
109
+ min-height: 0;
110
+ overflow: hidden;
111
+ }
112
+ @media (prefers-reduced-motion: reduce) {
113
+ svg,
114
+ details::details-content {
115
+ transition: none;
116
+ }
117
+ }
87
118
  </style>
88
119
 
89
120
  <details _open="@open" ontoggle="toggled(this)">
@@ -97,6 +128,10 @@
97
128
 
98
129
  <custom-element name="ui-accordion-trigger" slot="trigger">
99
130
  <style>
131
+ :host,
132
+ * {
133
+ touch-action: manipulation;
134
+ }
100
135
  :host {
101
136
  display: flex;
102
137
  flex: 1;
@@ -110,6 +145,10 @@
110
145
 
111
146
  <custom-element name="ui-accordion-content">
112
147
  <style>
148
+ :host,
149
+ * {
150
+ touch-action: manipulation;
151
+ }
113
152
  :host {
114
153
  display: block;
115
154
  padding-bottom: calc(var(--spacing) * 4);
@@ -61,6 +61,12 @@
61
61
  </script>
62
62
 
63
63
  <style>
64
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
65
+ touch-action does not reach shadow content in iOS Safari. */
66
+ :host,
67
+ * {
68
+ touch-action: manipulation;
69
+ }
64
70
  :host {
65
71
  display: contents;
66
72
  }
@@ -68,6 +74,10 @@
68
74
  box-sizing: border-box;
69
75
  width: 100%;
70
76
  max-width: min(calc(var(--spacing) * 128), calc(100% - calc(var(--spacing) * 8)));
77
+ /* See ui-dialog: an explicit height keeps it from stretching, svh from resizing on scroll. */
78
+ height: fit-content;
79
+ max-height: calc(100svh - calc(var(--spacing) * 8));
80
+ overflow: auto;
71
81
  padding: calc(var(--spacing) * 6);
72
82
  border: 1px solid var(--border);
73
83
  border-radius: var(--radius-lg);
@@ -77,6 +87,8 @@
77
87
  }
78
88
  dialog[open] {
79
89
  display: grid;
90
+ /* Safari stretches a grid overlay to its max-height unless the rows are packed at the start. */
91
+ align-content: start;
80
92
  gap: calc(var(--spacing) * 4);
81
93
  }
82
94
  dialog::backdrop {
@@ -101,6 +113,10 @@
101
113
 
102
114
  <custom-element name="ui-alert-dialog-trigger" slot="trigger">
103
115
  <style>
116
+ :host,
117
+ * {
118
+ touch-action: manipulation;
119
+ }
104
120
  :host,
105
121
  span {
106
122
  display: contents;
@@ -112,6 +128,10 @@
112
128
 
113
129
  <custom-element name="ui-alert-dialog-header" slot="header">
114
130
  <style>
131
+ :host,
132
+ * {
133
+ touch-action: manipulation;
134
+ }
115
135
  :host {
116
136
  display: grid;
117
137
  gap: calc(var(--spacing) * 2);
@@ -123,6 +143,10 @@
123
143
 
124
144
  <custom-element name="ui-alert-dialog-title">
125
145
  <style>
146
+ :host,
147
+ * {
148
+ touch-action: manipulation;
149
+ }
126
150
  :host {
127
151
  display: block;
128
152
  }
@@ -138,6 +162,10 @@
138
162
 
139
163
  <custom-element name="ui-alert-dialog-description">
140
164
  <style>
165
+ :host,
166
+ * {
167
+ touch-action: manipulation;
168
+ }
141
169
  :host {
142
170
  display: block;
143
171
  }
@@ -153,6 +181,10 @@
153
181
 
154
182
  <custom-element name="ui-alert-dialog-footer" slot="footer">
155
183
  <style>
184
+ :host,
185
+ * {
186
+ touch-action: manipulation;
187
+ }
156
188
  :host {
157
189
  display: flex;
158
190
  flex-wrap: wrap;
@@ -173,6 +205,10 @@
173
205
  </script>
174
206
 
175
207
  <style>
208
+ :host,
209
+ * {
210
+ touch-action: manipulation;
211
+ }
176
212
  :host {
177
213
  display: inline-flex;
178
214
  }
@@ -187,6 +223,10 @@
187
223
  </script>
188
224
 
189
225
  <style>
226
+ :host,
227
+ * {
228
+ touch-action: manipulation;
229
+ }
190
230
  :host {
191
231
  display: inline-flex;
192
232
  }
@@ -6,6 +6,12 @@
6
6
  </script>
7
7
 
8
8
  <style>
9
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
10
+ touch-action does not reach shadow content in iOS Safari. */
11
+ :host,
12
+ * {
13
+ touch-action: manipulation;
14
+ }
9
15
  :host {
10
16
  position: relative;
11
17
  display: grid;
@@ -44,6 +50,10 @@
44
50
 
45
51
  <custom-element name="ui-alert-title">
46
52
  <style>
53
+ :host,
54
+ * {
55
+ touch-action: manipulation;
56
+ }
47
57
  :host {
48
58
  display: block;
49
59
  min-height: calc(var(--spacing) * 4);
@@ -57,6 +67,10 @@
57
67
 
58
68
  <custom-element name="ui-alert-description">
59
69
  <style>
70
+ :host,
71
+ * {
72
+ touch-action: manipulation;
73
+ }
60
74
  :host {
61
75
  display: grid;
62
76
  justify-items: start;
@@ -4,6 +4,12 @@
4
4
  </script>
5
5
 
6
6
  <style>
7
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
8
+ touch-action does not reach shadow content in iOS Safari. */
9
+ :host,
10
+ * {
11
+ touch-action: manipulation;
12
+ }
7
13
  :host {
8
14
  display: block;
9
15
  position: relative;
@@ -4,6 +4,12 @@
4
4
  </script>
5
5
 
6
6
  <style>
7
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
8
+ touch-action does not reach shadow content in iOS Safari. */
9
+ :host,
10
+ * {
11
+ touch-action: manipulation;
12
+ }
7
13
  :host {
8
14
  display: inline-flex;
9
15
  align-items: center;
@@ -48,6 +54,10 @@
48
54
 
49
55
  <custom-element name="ui-attachment-media" slot="media">
50
56
  <style>
57
+ :host,
58
+ * {
59
+ touch-action: manipulation;
60
+ }
51
61
  :host {
52
62
  display: grid;
53
63
  place-content: center;
@@ -74,6 +84,10 @@
74
84
 
75
85
  <custom-element name="ui-attachment-actions" slot="actions">
76
86
  <style>
87
+ :host,
88
+ * {
89
+ touch-action: manipulation;
90
+ }
77
91
  :host {
78
92
  display: flex;
79
93
  gap: calc(var(--spacing) * 1);
@@ -18,6 +18,12 @@
18
18
  </script>
19
19
 
20
20
  <style>
21
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
22
+ touch-action does not reach shadow content in iOS Safari. */
23
+ :host,
24
+ * {
25
+ touch-action: manipulation;
26
+ }
21
27
  :host {
22
28
  position: relative;
23
29
  display: inline-flex;
@@ -55,6 +61,10 @@
55
61
  </script>
56
62
 
57
63
  <style>
64
+ :host,
65
+ * {
66
+ touch-action: manipulation;
67
+ }
58
68
  :host {
59
69
  display: contents;
60
70
  }
@@ -72,6 +82,10 @@
72
82
 
73
83
  <custom-element name="ui-avatar-fallback">
74
84
  <style>
85
+ :host,
86
+ * {
87
+ touch-action: manipulation;
88
+ }
75
89
  :host {
76
90
  display: contents;
77
91
  }
@@ -92,6 +106,10 @@
92
106
 
93
107
  <custom-element name="ui-avatar-group" role="group">
94
108
  <style>
109
+ :host,
110
+ * {
111
+ touch-action: manipulation;
112
+ }
95
113
  :host {
96
114
  display: flex;
97
115
  align-items: center;
@@ -109,6 +127,10 @@
109
127
 
110
128
  <custom-element name="ui-avatar-group-count">
111
129
  <style>
130
+ :host,
131
+ * {
132
+ touch-action: manipulation;
133
+ }
112
134
  :host {
113
135
  display: inline-flex;
114
136
  flex-shrink: 0;
@@ -7,6 +7,12 @@
7
7
  </script>
8
8
 
9
9
  <style>
10
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
11
+ touch-action does not reach shadow content in iOS Safari. */
12
+ :host,
13
+ * {
14
+ touch-action: manipulation;
15
+ }
10
16
  :host {
11
17
  display: inline-flex;
12
18
  align-items: center;
@@ -4,6 +4,12 @@
4
4
  </script>
5
5
 
6
6
  <style>
7
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
8
+ touch-action does not reach shadow content in iOS Safari. */
9
+ :host,
10
+ * {
11
+ touch-action: manipulation;
12
+ }
7
13
  :host {
8
14
  display: block;
9
15
  }
@@ -14,6 +20,10 @@
14
20
 
15
21
  <custom-element name="ui-breadcrumb-list">
16
22
  <style>
23
+ :host,
24
+ * {
25
+ touch-action: manipulation;
26
+ }
17
27
  :host {
18
28
  display: block;
19
29
  }
@@ -36,6 +46,10 @@
36
46
 
37
47
  <custom-element name="ui-breadcrumb-item" role="listitem">
38
48
  <style>
49
+ :host,
50
+ * {
51
+ touch-action: manipulation;
52
+ }
39
53
  :host {
40
54
  display: inline-flex;
41
55
  align-items: center;
@@ -52,6 +66,10 @@
52
66
  </script>
53
67
 
54
68
  <style>
69
+ :host,
70
+ * {
71
+ touch-action: manipulation;
72
+ }
55
73
  a {
56
74
  color: inherit;
57
75
  text-decoration: none;
@@ -72,6 +90,10 @@
72
90
 
73
91
  <custom-element name="ui-breadcrumb-page">
74
92
  <style>
93
+ :host,
94
+ * {
95
+ touch-action: manipulation;
96
+ }
75
97
  span {
76
98
  font-weight: var(--font-weight-normal);
77
99
  color: var(--foreground);
@@ -87,6 +109,10 @@
87
109
  </script>
88
110
 
89
111
  <style>
112
+ :host,
113
+ * {
114
+ touch-action: manipulation;
115
+ }
90
116
  :host {
91
117
  display: inline-flex;
92
118
  align-items: center;
@@ -103,6 +129,10 @@
103
129
 
104
130
  <custom-element name="ui-breadcrumb-ellipsis">
105
131
  <style>
132
+ :host,
133
+ * {
134
+ touch-action: manipulation;
135
+ }
106
136
  :host {
107
137
  display: inline-flex;
108
138
  align-items: center;
@@ -7,6 +7,12 @@
7
7
  </script>
8
8
 
9
9
  <style>
10
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
11
+ touch-action does not reach shadow content in iOS Safari. */
12
+ :host,
13
+ * {
14
+ touch-action: manipulation;
15
+ }
10
16
  :host {
11
17
  display: flex;
12
18
  flex-direction: column;
@@ -56,6 +62,10 @@
56
62
 
57
63
  <custom-element name="ui-bubble-reactions" slot="reactions">
58
64
  <style>
65
+ :host,
66
+ * {
67
+ touch-action: manipulation;
68
+ }
59
69
  :host {
60
70
  display: flex;
61
71
  flex-wrap: wrap;
@@ -4,6 +4,12 @@
4
4
  </script>
5
5
 
6
6
  <style>
7
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
8
+ touch-action does not reach shadow content in iOS Safari. */
9
+ :host,
10
+ * {
11
+ touch-action: manipulation;
12
+ }
7
13
  :host {
8
14
  display: inline-flex;
9
15
  align-items: stretch;
@@ -33,6 +39,10 @@
33
39
 
34
40
  <custom-element name="ui-button-group-text">
35
41
  <style>
42
+ :host,
43
+ * {
44
+ touch-action: manipulation;
45
+ }
36
46
  :host {
37
47
  display: flex;
38
48
  align-items: center;
@@ -69,6 +79,10 @@
69
79
  </script>
70
80
 
71
81
  <style>
82
+ :host,
83
+ * {
84
+ touch-action: manipulation;
85
+ }
72
86
  :host {
73
87
  position: relative;
74
88
  z-index: 1;
@@ -11,6 +11,9 @@
11
11
  popovertargetaction: prop.enum(["toggle", "show", "hide"]),
12
12
  commandfor: prop.string(),
13
13
  command: prop.string(),
14
+ href: prop.string(),
15
+ target: prop.string(),
16
+ rel: prop.string(),
14
17
  });
15
18
 
16
19
  const NATIVE_COMMANDS = "commandForElement" in HTMLButtonElement.prototype;
@@ -62,6 +65,12 @@
62
65
  </script>
63
66
 
64
67
  <style>
68
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
69
+ touch-action does not reach shadow content in iOS Safari. */
70
+ :host,
71
+ * {
72
+ touch-action: manipulation;
73
+ }
65
74
  :host {
66
75
  display: inline-flex;
67
76
  vertical-align: middle;
@@ -69,7 +78,11 @@
69
78
  :host([hidden]) {
70
79
  display: none;
71
80
  }
72
- button {
81
+ is-visible,
82
+ is-hidden {
83
+ display: contents;
84
+ }
85
+ .control {
73
86
  all: unset;
74
87
  box-sizing: border-box;
75
88
  display: inline-flex;
@@ -97,14 +110,15 @@
97
110
  }
98
111
  /* Mixing toward the text colour darkens a light-mode fill and lightens a dark-mode one, so
99
112
  hover never lowers the text contrast. */
100
- button:hover {
113
+ .control:hover {
101
114
  background: color-mix(in oklab, var(--primary) 88%, var(--foreground));
102
115
  }
103
- button:focus-visible {
116
+ .control:focus-visible {
104
117
  border-color: var(--ring);
105
118
  box-shadow: var(--focus-ring);
106
119
  }
107
- button:disabled {
120
+ .control:disabled,
121
+ :host([disabled]) a.control {
108
122
  pointer-events: none;
109
123
  opacity: 0.5;
110
124
  }
@@ -114,75 +128,80 @@
114
128
  flex-shrink: 0;
115
129
  pointer-events: none;
116
130
  }
117
- :host(:state(attr-variant-is-destructive)) button {
131
+ :host(:state(attr-variant-is-destructive)) .control {
118
132
  background: var(--destructive);
119
133
  color: var(--destructive-foreground);
120
134
  }
121
- :host(:state(attr-variant-is-destructive)) button:hover {
135
+ :host(:state(attr-variant-is-destructive)) .control:hover {
122
136
  background: color-mix(in oklab, var(--destructive) 88%, var(--foreground));
123
137
  }
124
- :host(:state(attr-variant-is-outline)) button {
138
+ :host(:state(attr-variant-is-outline)) .control {
125
139
  border-color: var(--border);
126
140
  background: var(--background);
127
141
  color: var(--foreground);
128
142
  box-shadow: var(--shadow-xs);
129
143
  }
130
- :host(:state(attr-variant-is-outline)) button:hover,
131
- :host(:state(attr-variant-is-ghost)) button:hover {
144
+ :host(:state(attr-variant-is-outline)) .control:hover,
145
+ :host(:state(attr-variant-is-ghost)) .control:hover {
132
146
  background: var(--accent);
133
147
  color: var(--accent-foreground);
134
148
  }
135
- :host(:state(attr-variant-is-secondary)) button {
149
+ :host(:state(attr-variant-is-secondary)) .control {
136
150
  background: var(--secondary);
137
151
  color: var(--secondary-foreground);
138
152
  }
139
- :host(:state(attr-variant-is-secondary)) button:hover {
153
+ :host(:state(attr-variant-is-secondary)) .control:hover {
140
154
  background: color-mix(in oklab, var(--secondary) 80%, transparent);
141
155
  }
142
- :host(:state(attr-variant-is-ghost)) button {
156
+ :host(:state(attr-variant-is-ghost)) .control {
143
157
  background: transparent;
144
158
  color: var(--foreground);
145
159
  }
146
- :host(:state(attr-variant-is-link)) button {
160
+ :host(:state(attr-variant-is-link)) .control {
147
161
  background: transparent;
148
162
  color: var(--primary);
149
163
  text-underline-offset: 4px;
150
164
  }
151
- :host(:state(attr-variant-is-link)) button:hover {
165
+ :host(:state(attr-variant-is-link)) .control:hover {
152
166
  text-decoration: underline;
153
167
  }
154
- :host(:state(attr-size-is-xs)) button {
168
+ :host(:state(attr-size-is-xs)) .control {
155
169
  height: calc(var(--spacing) * 6);
156
170
  gap: calc(var(--spacing) * 1);
157
171
  padding: 0 calc(var(--spacing) * 2);
158
172
  font-size: var(--text-xs);
159
173
  }
160
- :host(:state(attr-size-is-sm)) button {
174
+ :host(:state(attr-size-is-sm)) .control {
161
175
  height: calc(var(--spacing) * 8);
162
176
  gap: calc(var(--spacing) * 1.5);
163
177
  padding: 0 calc(var(--spacing) * 3);
164
178
  }
165
- :host(:state(attr-size-is-lg)) button {
179
+ :host(:state(attr-size-is-lg)) .control {
166
180
  height: calc(var(--spacing) * 10);
167
181
  padding: 0 calc(var(--spacing) * 6);
168
182
  }
169
- :host(:is(:state(attr-size-is-icon), :state(attr-size-is-icon-xs), :state(attr-size-is-icon-sm), :state(attr-size-is-icon-lg))) button {
183
+ :host(:is(:state(attr-size-is-icon), :state(attr-size-is-icon-xs), :state(attr-size-is-icon-sm), :state(attr-size-is-icon-lg))) .control {
170
184
  padding: 0;
171
185
  width: calc(var(--spacing) * 9);
172
186
  }
173
- :host(:state(attr-size-is-icon-xs)) button {
187
+ :host(:state(attr-size-is-icon-xs)) .control {
174
188
  width: calc(var(--spacing) * 6);
175
189
  height: calc(var(--spacing) * 6);
176
190
  }
177
- :host(:state(attr-size-is-icon-sm)) button {
191
+ :host(:state(attr-size-is-icon-sm)) .control {
178
192
  width: calc(var(--spacing) * 8);
179
193
  height: calc(var(--spacing) * 8);
180
194
  }
181
- :host(:state(attr-size-is-icon-lg)) button {
195
+ :host(:state(attr-size-is-icon-lg)) .control {
182
196
  width: calc(var(--spacing) * 10);
183
197
  height: calc(var(--spacing) * 10);
184
198
  }
185
199
  </style>
186
200
 
187
- <button type="button" ref="button" part="button" _aria-label="@label" _aria-description="@description" _disabled="@disabled" onclick="activate()"><slot></slot></button>
201
+ <is-visible when="@href">
202
+ <a class="control" ref="link" part="button" _href="@href" _target="@target" _rel="@rel" _aria-label="@label" _aria-description="@description"><slot></slot></a>
203
+ </is-visible>
204
+ <is-hidden when="@href">
205
+ <button class="control" type="button" ref="button" part="button" _aria-label="@label" _aria-description="@description" _disabled="@disabled" onclick="activate()"><slot></slot></button>
206
+ </is-hidden>
188
207
  </custom-element>
@@ -80,6 +80,12 @@
80
80
  </script>
81
81
 
82
82
  <style>
83
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
84
+ touch-action does not reach shadow content in iOS Safari. */
85
+ :host,
86
+ * {
87
+ touch-action: manipulation;
88
+ }
83
89
  :host {
84
90
  display: inline-block;
85
91
  padding: calc(var(--spacing) * 3);