@rcarls/rc-fab 0.3.2 → 0.4.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,58 @@
1
+ # @rcarls/rc-fab
2
+
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @rcarls/rc-common@0.4.1
8
+
9
+ ## 0.4.0
10
+
11
+ ### Patch Changes
12
+
13
+ - 55e8ab5: Preserve UA-like control and surface styling until an optional theme supplies decorative tokens.
14
+ Restore themed button hover and pressed state layers, including a pointer-origin Material ripple.
15
+ - 9a4aa64: Fix `rc-fab`'s CSS custom property documentation to match the component's
16
+ actual `revert`/unset fallbacks (background, color, radius, shadow, size,
17
+ gap, padding, font, focus-ring, and transition properties all defer to the
18
+ native button or a packaged theme, not the previously documented literal
19
+ defaults), and document `position`, `scroll-reveal`, and the computed
20
+ `scroll-below-threshold` attribute.
21
+ - Updated dependencies [e57277f]
22
+ - Updated dependencies [ccca8e2]
23
+ - Updated dependencies [037b1b3]
24
+ - @rcarls/rc-common@0.4.0
25
+
26
+ ## 0.3.2
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [88b4086]
31
+ - @rcarls/rc-common@0.3.2
32
+
33
+ ## 0.3.1
34
+
35
+ ### Patch Changes
36
+
37
+ - @rcarls/rc-common@0.3.1
38
+
39
+ ## 0.3.0
40
+
41
+ ### Added
42
+
43
+ - Add scroll-reveal behavior for back-to-top floating action buttons.
44
+
45
+ ### Changed
46
+
47
+ - Redesign the default behavior around the back-to-top use case.
48
+ - Update package metadata, README intro, and docs links.
49
+
50
+ ### Fixed
51
+
52
+ - Generalize published FAB examples.
53
+
54
+ ### Dependencies
55
+
56
+ - Sync internal dependencies to 0.3.0.
57
+
58
+ ## 0.2.0
package/README.md CHANGED
@@ -61,11 +61,11 @@ import '@rcarls/rc-fab/define';
61
61
  | `--rc-fab-position` | `fixed` | CSS position value. Use `absolute` for layout-relative placement |
62
62
  | `--rc-fab-scroll-threshold` | `300px` | Scroll distance before the FAB appears (requires `scroll-reveal`) |
63
63
  | `--rc-fab-scroll-timeline` | `scroll(root block)` | Override scroll target for the CSS animation path (e.g. `scroll(nearest block)`) |
64
- | `--rc-fab-radius` | `9999px` | Border-radius. Default is pill-shaped. Use `50%` for a circle or `1rem` for Material rounded-square |
65
- | `--rc-fab-size` | `3.5rem` | Height and minimum width |
66
- | `--rc-fab-bg` | `ButtonFace` | Button background colour |
67
- | `--rc-fab-color` | `ButtonText` | Button foreground colour |
68
- | `--rc-fab-shadow` | `none` | Elevation shadow |
64
+ | `--rc-fab-radius` | _(native)_ | Border-radius; defers to the native button's radius until set. Use `9999px` for pill-shaped, `50%` for a circle, or `1rem` for Material rounded-square |
65
+ | `--rc-fab-size` | _(native)_ | Height and minimum width; defers to native button sizing until set (packaged themes set `3.5rem`) |
66
+ | `--rc-fab-bg` | _(native)_ | Button background color; defers to the native button's background until set |
67
+ | `--rc-fab-color` | _(native)_ | Button foreground color; defers to the native button's color until set |
68
+ | `--rc-fab-shadow` | _(native)_ | Elevation shadow; defers to the native button's shadow until set |
69
69
  | `--rc-fab-inset-block` | `1.5rem` | Distance from the block-axis edge |
70
70
  | `--rc-fab-inset-inline` | `1.5rem` | Distance from the inline-axis edge |
71
71
 
@@ -70,94 +70,92 @@
70
70
  "default": "10"
71
71
  },
72
72
  {
73
- "description": "Button background colour.",
74
- "name": "--rc-fab-bg",
75
- "default": "ButtonFace"
73
+ "description": "Gap between icon and label text. Unset by default; packaged themes such as `rc-theme-material` set `0.5rem`.",
74
+ "name": "--rc-fab-gap"
76
75
  },
77
76
  {
78
- "description": "Hover background colour.",
79
- "name": "--rc-fab-bg-hover",
80
- "default": "var(--rc-fab-bg)"
77
+ "description": "Minimum inline size and block size of the button. Unset by default (native button sizing applies); packaged themes such as `rc-theme-material` set `3.5rem`.",
78
+ "name": "--rc-fab-size"
81
79
  },
82
80
  {
83
- "description": "Button foreground colour.",
84
- "name": "--rc-fab-color",
85
- "default": "ButtonText"
81
+ "description": "Button block-axis padding (defers to native button padding when unset).",
82
+ "name": "--rc-fab-padding-block"
86
83
  },
87
84
  {
88
- "description": "Height and minimum width.",
89
- "name": "--rc-fab-size",
90
- "default": "3.5rem"
85
+ "description": "Button inline-axis padding (defers to native button padding when unset).",
86
+ "name": "--rc-fab-padding-inline"
91
87
  },
92
88
  {
93
- "description": "Border-radius. Default is pill-shaped. Override to `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.",
94
- "name": "--rc-fab-radius",
95
- "default": "9999px"
89
+ "description": "Button background (defers to native button background when unset).",
90
+ "name": "--rc-fab-bg"
96
91
  },
97
92
  {
98
- "description": "Elevation shadow.",
99
- "name": "--rc-fab-shadow",
100
- "default": "none"
93
+ "description": "Button foreground color (defers to native button color when unset).",
94
+ "name": "--rc-fab-color"
101
95
  },
102
96
  {
103
- "description": "Hover shadow.",
104
- "name": "--rc-fab-shadow-hover",
105
- "default": "var(--rc-fab-shadow)"
97
+ "description": "Button border (defers to native button border when unset).",
98
+ "name": "--rc-fab-border"
106
99
  },
107
100
  {
108
- "description": "Pressed shadow.",
109
- "name": "--rc-fab-shadow-active",
110
- "default": "none"
101
+ "description": "Button border-radius (defers to native button radius when unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.",
102
+ "name": "--rc-fab-radius"
111
103
  },
112
104
  {
113
- "description": "Inline padding.",
114
- "name": "--rc-fab-padding-inline",
115
- "default": "1rem"
105
+ "description": "Elevation shadow (defers to native button shadow when unset).",
106
+ "name": "--rc-fab-shadow"
116
107
  },
117
108
  {
118
- "description": "Gap between icon and label text.",
119
- "name": "--rc-fab-gap",
120
- "default": "0.5rem"
109
+ "description": "Font family for label text (defers to native button font when unset).",
110
+ "name": "--rc-fab-font-family"
121
111
  },
122
112
  {
123
- "description": "Font family for label text.",
124
- "name": "--rc-fab-font-family",
125
- "default": "inherit"
113
+ "description": "Font size for label text (defers to native button font when unset).",
114
+ "name": "--rc-fab-font-size"
126
115
  },
127
116
  {
128
- "description": "Font size for label text.",
129
- "name": "--rc-fab-font-size",
130
- "default": "0.875rem"
117
+ "description": "Font weight for label text (defers to native button font when unset).",
118
+ "name": "--rc-fab-font-weight"
131
119
  },
132
120
  {
133
- "description": "Font weight for label text.",
134
- "name": "--rc-fab-font-weight",
135
- "default": "500"
121
+ "description": "Letter spacing for label text (defers to native button styling when unset).",
122
+ "name": "--rc-fab-letter-spacing"
136
123
  },
137
124
  {
138
- "description": "Letter spacing for label text.",
139
- "name": "--rc-fab-letter-spacing",
140
- "default": "0.00625em"
125
+ "description": "Transition shorthand for hover/active state changes (defers to native button transition when unset).",
126
+ "name": "--rc-fab-transition"
141
127
  },
142
128
  {
143
- "description": "Focus ring style.",
144
- "name": "--rc-fab-focus-ring",
145
- "default": "2px solid currentColor"
129
+ "description": "Hover background (defers to native `:hover` styling when unset).",
130
+ "name": "--rc-fab-bg-hover"
146
131
  },
147
132
  {
148
- "description": "Focus ring offset.",
149
- "name": "--rc-fab-focus-ring-offset",
150
- "default": "2px"
133
+ "description": "Hover shadow (defers to native `:hover` styling when unset).",
134
+ "name": "--rc-fab-shadow-hover"
151
135
  },
152
136
  {
153
- "description": "Opacity applied when the button is disabled.",
154
- "name": "--rc-fab-disabled-opacity",
155
- "default": "0.38"
137
+ "description": "Pressed shadow (defers to native `:active` styling when unset).",
138
+ "name": "--rc-fab-shadow-active"
156
139
  },
157
140
  {
158
- "description": "Transition speed for hover and active states.",
159
- "name": "--rc-fab-transition-duration",
160
- "default": "200ms"
141
+ "description": "Transform applied while pressed, e.g. `scale(0.96)` (defers to native `:active` styling when unset).",
142
+ "name": "--rc-fab-active-transform"
143
+ },
144
+ {
145
+ "description": "Focus ring style (defers to native `:focus-visible` styling when unset).",
146
+ "name": "--rc-fab-focus-ring"
147
+ },
148
+ {
149
+ "description": "Focus ring offset (defers to native `:focus-visible` styling when unset).",
150
+ "name": "--rc-fab-focus-ring-offset"
151
+ },
152
+ {
153
+ "description": "Opacity applied when the button is disabled (defers to native `:disabled` styling when unset).",
154
+ "name": "--rc-fab-disabled-opacity"
155
+ },
156
+ {
157
+ "description": "Shadow applied when the button is disabled (defers to native `:disabled` styling when unset).",
158
+ "name": "--rc-fab-disabled-shadow"
161
159
  },
162
160
  {
163
161
  "description": "Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.",
@@ -230,22 +228,26 @@
230
228
  ],
231
229
  "attributes": [
232
230
  {
231
+ "description": "Viewport corner where the FAB is anchored. Uses logical inline/block directions.",
233
232
  "name": "position",
234
233
  "type": {
235
234
  "text": "'bottom-end' | 'bottom-start' | 'top-end' | 'top-start'"
236
235
  },
237
236
  "default": "'bottom-end'",
238
- "description": "Viewport corner where the FAB is anchored. Uses logical inline/block directions.",
239
237
  "fieldName": "position"
240
238
  },
241
239
  {
240
+ "description": "Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers.",
242
241
  "name": "scroll-reveal",
243
242
  "type": {
244
243
  "text": "boolean"
245
244
  },
246
245
  "default": "false",
247
- "description": "Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers.",
248
246
  "fieldName": "scrollReveal"
247
+ },
248
+ {
249
+ "description": "Present when `scroll-reveal` is active and the page hasn't scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation fallback; unused when the browser supports `animation-timeline: scroll()`.",
250
+ "name": "scroll-below-threshold"
249
251
  }
250
252
  ],
251
253
  "superclass": {
@@ -1,6 +1,6 @@
1
- import { css as b, LitElement as d, html as h } from "lit";
1
+ import { css as b, LitElement as v, html as d } from "lit";
2
2
  import { property as n } from "lit/decorators.js";
3
- import { NativeChildController as v, ScrollObserverController as f, findNearestScrollAncestor as p } from "@rcarls/rc-common";
3
+ import { NativeChildController as h, ScrollObserverController as f, findNearestScrollAncestor as p } from "@rcarls/rc-common";
4
4
  const m = b`
5
5
  :host {
6
6
  position: var(--rc-fab-position, fixed);
@@ -35,52 +35,48 @@ const m = b`
35
35
  display: inline-flex;
36
36
  align-items: center;
37
37
  justify-content: center;
38
- gap: var(--rc-fab-gap, 0.5rem);
38
+ gap: var(--rc-fab-gap);
39
39
 
40
- min-width: var(--rc-fab-size, 3.5rem);
41
- height: var(--rc-fab-size, 3.5rem);
42
- padding-block: 0;
43
- padding-inline: var(--rc-fab-padding-inline, 1rem);
40
+ min-width: var(--rc-fab-size);
41
+ height: var(--rc-fab-size);
42
+ padding-block: var(--rc-fab-padding-block, revert);
43
+ padding-inline: var(--rc-fab-padding-inline, revert);
44
44
 
45
- background: var(--rc-fab-bg, ButtonFace);
46
- color: var(--rc-fab-color, ButtonText);
45
+ background: var(--rc-fab-bg, revert);
46
+ color: var(--rc-fab-color, revert);
47
47
 
48
- border: none;
49
- border-radius: var(--rc-fab-radius, 9999px);
50
- box-shadow: var(--rc-fab-shadow, var(--rc-shadow-level2, none));
48
+ border: var(--rc-fab-border, revert);
49
+ border-radius: var(--rc-fab-radius, revert);
50
+ box-shadow: var(--rc-fab-shadow, revert);
51
51
 
52
- font-family: var(--rc-fab-font-family, var(--rc-font-family, inherit));
53
- font-size: var(--rc-fab-font-size, var(--rc-font-size, 0.875rem));
54
- font-weight: var(--rc-fab-font-weight, 500);
55
- letter-spacing: var(--rc-fab-letter-spacing, 0.00625em);
52
+ font-family: var(--rc-fab-font-family, revert);
53
+ font-size: var(--rc-fab-font-size, revert);
54
+ font-weight: var(--rc-fab-font-weight, revert);
55
+ letter-spacing: var(--rc-fab-letter-spacing, revert);
56
56
  white-space: nowrap;
57
57
 
58
- cursor: pointer;
59
- transition:
60
- background var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,
61
- box-shadow var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,
62
- transform var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease;
58
+ transition: var(--rc-fab-transition, revert);
63
59
  }
64
60
 
65
61
  :host(:has(button:hover)) ::slotted(button) {
66
- background: var(--rc-fab-bg-hover, var(--rc-fab-bg, ButtonFace));
67
- box-shadow: var(--rc-fab-shadow-hover, var(--rc-fab-shadow, var(--rc-shadow-level3, none)));
62
+ background: var(--rc-fab-bg-hover, revert);
63
+ box-shadow: var(--rc-fab-shadow-hover, revert);
68
64
  }
69
65
 
70
66
  :host(:has(button:active)) ::slotted(button) {
71
- box-shadow: var(--rc-fab-shadow-active, none);
72
- transform: scale(0.96);
67
+ box-shadow: var(--rc-fab-shadow-active, revert);
68
+ transform: var(--rc-fab-active-transform, revert);
73
69
  }
74
70
 
75
71
  :host(:has(button:focus-visible)) ::slotted(button) {
76
- outline: var(--rc-fab-focus-ring, var(--rc-focus-ring, 2px solid currentColor));
77
- outline-offset: var(--rc-fab-focus-ring-offset, var(--rc-focus-ring-offset, 2px));
72
+ outline: var(--rc-fab-focus-ring, revert);
73
+ outline-offset: var(--rc-fab-focus-ring-offset, revert);
78
74
  }
79
75
 
80
76
  :host(:has(button:disabled)) ::slotted(button) {
81
- opacity: var(--rc-fab-disabled-opacity, var(--rc-disabled-opacity, 0.38));
77
+ opacity: var(--rc-fab-disabled-opacity, revert);
82
78
  pointer-events: none;
83
- box-shadow: none;
79
+ box-shadow: var(--rc-fab-disabled-shadow, revert);
84
80
  }
85
81
 
86
82
  @keyframes rc-fab-scroll-reveal {
@@ -165,40 +161,41 @@ const m = b`
165
161
  }
166
162
  }
167
163
  `;
168
- var u = Object.defineProperty, c = (a, t, r, y) => {
169
- for (var e = void 0, i = a.length - 1, l; i >= 0; i--)
170
- (l = a[i]) && (e = l(t, r, e) || e);
171
- return e && u(t, r, e), e;
164
+ var u = Object.defineProperty, c = (l, e, r, y) => {
165
+ for (var t = void 0, i = l.length - 1, a; i >= 0; i--)
166
+ (a = l[i]) && (t = a(e, r, t) || t);
167
+ return t && u(e, r, t), t;
172
168
  };
173
- const s = class s extends d {
169
+ const s = class s extends v {
174
170
  constructor() {
175
- super(), this.position = "bottom-end", this.scrollReveal = !1, new v(this, {
171
+ super(), this.position = "bottom-end", this.scrollReveal = !1, new h(this, {
176
172
  selector: ":scope > button",
177
173
  observe: !0,
178
174
  onMissing: () => {
179
175
  }
180
176
  });
181
177
  }
182
- updated(t) {
183
- super.updated(t), (t.has("scrollReveal") || this.scrollReveal && !this._scrollCtrl) && this._syncScrollFallback();
178
+ updated(e) {
179
+ super.updated(e), (e.has("scrollReveal") || this.scrollReveal && !this._scrollCtrl) && this._syncScrollFallback();
184
180
  }
185
181
  _syncScrollFallback() {
186
- if (this._scrollCtrl && (this._scrollCtrl.setOptions({ disabled: !0 }), this.removeController(this._scrollCtrl), this._scrollCtrl = void 0, this.removeAttribute("scroll-below-threshold")), !this.scrollReveal || CSS.supports("animation-timeline: scroll()")) return;
187
- const t = this._getThreshold();
182
+ if (this._scrollCtrl && (this._scrollCtrl.setOptions({ disabled: !0 }), this.removeController(this._scrollCtrl), this._scrollCtrl = void 0, this.removeAttribute("scroll-below-threshold")), !this.scrollReveal || CSS.supports("animation-timeline: scroll()"))
183
+ return;
184
+ const e = this._getThreshold();
188
185
  this._scrollCtrl = new f(this, {
189
186
  target: () => p(this),
190
- threshold: t,
187
+ threshold: e,
191
188
  onScroll: (r) => {
192
- this.toggleAttribute("scroll-below-threshold", r < t);
189
+ this.toggleAttribute("scroll-below-threshold", r < e);
193
190
  }
194
191
  });
195
192
  }
196
193
  _getThreshold() {
197
- const t = getComputedStyle(this).getPropertyValue("--rc-fab-scroll-threshold").trim(), r = parseFloat(t);
194
+ const e = getComputedStyle(this).getPropertyValue("--rc-fab-scroll-threshold").trim(), r = parseFloat(e);
198
195
  return Number.isFinite(r) ? r : 300;
199
196
  }
200
197
  render() {
201
- return h`<slot></slot>`;
198
+ return d`<slot></slot>`;
202
199
  }
203
200
  };
204
201
  s.styles = m;
@@ -212,4 +209,4 @@ c([
212
209
  export {
213
210
  o as R
214
211
  };
215
- //# sourceMappingURL=rc-fab-CKowGgDZ.js.map
212
+ //# sourceMappingURL=rc-fab-CdJ-peVY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-fab-CdJ-peVY.js","sources":["../src/rc-fab.styles.ts","../src/rc-fab.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const fabStyles = css`\n :host {\n position: var(--rc-fab-position, fixed);\n inset-block-end: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: var(--rc-fab-inset-inline, 1.5rem);\n z-index: var(--rc-fab-z-index, 10);\n display: inline-flex;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([position='bottom-start']) {\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n :host([position='top-end']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n }\n\n :host([position='top-start']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n ::slotted(button) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-fab-gap);\n\n min-width: var(--rc-fab-size);\n height: var(--rc-fab-size);\n padding-block: var(--rc-fab-padding-block, revert);\n padding-inline: var(--rc-fab-padding-inline, revert);\n\n background: var(--rc-fab-bg, revert);\n color: var(--rc-fab-color, revert);\n\n border: var(--rc-fab-border, revert);\n border-radius: var(--rc-fab-radius, revert);\n box-shadow: var(--rc-fab-shadow, revert);\n\n font-family: var(--rc-fab-font-family, revert);\n font-size: var(--rc-fab-font-size, revert);\n font-weight: var(--rc-fab-font-weight, revert);\n letter-spacing: var(--rc-fab-letter-spacing, revert);\n white-space: nowrap;\n\n transition: var(--rc-fab-transition, revert);\n }\n\n :host(:has(button:hover)) ::slotted(button) {\n background: var(--rc-fab-bg-hover, revert);\n box-shadow: var(--rc-fab-shadow-hover, revert);\n }\n\n :host(:has(button:active)) ::slotted(button) {\n box-shadow: var(--rc-fab-shadow-active, revert);\n transform: var(--rc-fab-active-transform, revert);\n }\n\n :host(:has(button:focus-visible)) ::slotted(button) {\n outline: var(--rc-fab-focus-ring, revert);\n outline-offset: var(--rc-fab-focus-ring-offset, revert);\n }\n\n :host(:has(button:disabled)) ::slotted(button) {\n opacity: var(--rc-fab-disabled-opacity, revert);\n pointer-events: none;\n box-shadow: var(--rc-fab-disabled-shadow, revert);\n }\n\n @keyframes rc-fab-scroll-reveal {\n from {\n opacity: 0;\n visibility: hidden;\n }\n 50% {\n visibility: visible;\n }\n to {\n opacity: 1;\n visibility: visible;\n }\n }\n\n @supports (animation-timeline: scroll()) {\n :host([scroll-reveal]) {\n /*\n * Override for embedded/demo contexts where root does not scroll:\n * style=\"--rc-fab-scroll-timeline: scroll(nearest block)\"\n */\n --rc-fab-scroll-timeline: scroll(root block);\n\n animation-name: rc-fab-scroll-reveal;\n animation-duration: 1ms; /* required; scroll position drives progress, not time */\n animation-timing-function: linear;\n animation-fill-mode: both;\n animation-timeline: var(--rc-fab-scroll-timeline);\n animation-range: calc(var(--rc-fab-scroll-threshold, 300px) - 100px)\n var(--rc-fab-scroll-threshold, 300px);\n }\n\n /* Keyboard escape hatch: always visible when the button has focus */\n :host([scroll-reveal]):has(button:focus-visible) {\n animation: none;\n opacity: 1;\n visibility: visible;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]) {\n animation-range: var(--rc-fab-scroll-threshold, 300px) var(--rc-fab-scroll-threshold, 300px);\n }\n }\n }\n\n /*\n * JS fallback for browsers without scroll-driven animation support.\n * [scroll-below-threshold] is toggled by ScrollObserverController;\n * transitions replicate the CSS animation's show/hide behavior.\n */\n @supports not (animation-timeline: scroll()) {\n /* visible state: visibility snaps immediately, opacity fades in */\n :host([scroll-reveal]) {\n transition:\n opacity 200ms linear,\n visibility 0s linear;\n }\n\n /* hidden state: opacity fades first, visibility snaps off after delay */\n :host([scroll-reveal][scroll-below-threshold]) {\n opacity: 0;\n visibility: hidden;\n transition:\n opacity 200ms linear,\n visibility 0s linear 200ms;\n }\n\n /* Keyboard escape hatch */\n :host([scroll-reveal][scroll-below-threshold]):has(button:focus-visible) {\n opacity: 1;\n visibility: visible;\n transition: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]),\n :host([scroll-reveal][scroll-below-threshold]) {\n transition: none;\n }\n }\n }\n`;\n\nexport default fabStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport {\n NativeChildController,\n ScrollObserverController,\n findNearestScrollAncestor,\n warnMissingDirectChild,\n} from '@rcarls/rc-common';\n\nimport fabStyles from './rc-fab.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-fab': RCFab;\n }\n}\n\n/**\n * Sticky floating action button modeled after the Material 3 Floating action button,\n * wrapping a consumer-supplied button with scroll-aware visibility.\n *\n * Adapted from the Material Design FAB component, use this for \"back to top\",\n * sticky CTAs, chat launchers, and of course as FABs in your Material Design PWA.\n *\n * Place a native `<button>` as the direct child. The button's own accessible\n * name (text content or `aria-label`) becomes the FAB's accessible name.\n *\n * Icons go inside the button alongside or instead of visible text.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-fab rc-fab docs}\n * @see {@link https://m3.material.io/components/floating-action-button/overview Material 3 Floating action button}\n *\n * @example Back to top (scroll-triggered)\n * ```html\n * <rc-fab scroll-reveal>\n * <button type=\"button\" aria-label=\"Back to top\" onclick=\"scrollTo({top:0,behavior:'smooth'})\">\n * <span aria-hidden=\"true\">↑</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Icon-only FAB (always visible)\n * ```html\n * <rc-fab>\n * <button type=\"button\" aria-label=\"Create\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">add</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Extended FAB (icon + visible label)\n * ```html\n * <rc-fab>\n * <button type=\"button\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">edit</span>\n * Compose\n * </button>\n * </rc-fab>\n * ```\n *\n * @slot default - The native `<button>` element. The button's own accessible\n * name (text content or `aria-label`) serves as the FAB's accessible name.\n *\n * @attr position - Viewport corner where the FAB is anchored.\n * @attr scroll-reveal - Reveal the FAB only after the page scrolls past\n * `--rc-fab-scroll-threshold`.\n * @attr [scroll-below-threshold] - Present when `scroll-reveal` is active and the page hasn't\n * scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation\n * fallback; unused when the browser supports `animation-timeline: scroll()`.\n *\n * @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.\n * @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.\n * @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.\n * @cssprop [--rc-fab-z-index=10] - Stacking order.\n * @cssprop [--rc-fab-gap] - Gap between icon and label text. Unset by default; packaged themes\n * such as `rc-theme-material` set `0.5rem`.\n * @cssprop [--rc-fab-size] - Minimum inline size and block size of the button. Unset by\n * default (native button sizing applies); packaged themes such as `rc-theme-material` set\n * `3.5rem`.\n * @cssprop [--rc-fab-padding-block] - Button block-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-fab-padding-inline] - Button inline-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-fab-bg] - Button background (defers to native button background when unset).\n * @cssprop [--rc-fab-color] - Button foreground color (defers to native button color when\n * unset).\n * @cssprop [--rc-fab-border] - Button border (defers to native button border when unset).\n * @cssprop [--rc-fab-radius] - Button border-radius (defers to native button radius when\n * unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for\n * Material rounded-square, etc.\n * @cssprop [--rc-fab-shadow] - Elevation shadow (defers to native button shadow when unset).\n * @cssprop [--rc-fab-font-family] - Font family for label text (defers to native button font\n * when unset).\n * @cssprop [--rc-fab-font-size] - Font size for label text (defers to native button font when\n * unset).\n * @cssprop [--rc-fab-font-weight] - Font weight for label text (defers to native button font\n * when unset).\n * @cssprop [--rc-fab-letter-spacing] - Letter spacing for label text (defers to native button\n * styling when unset).\n * @cssprop [--rc-fab-transition] - Transition shorthand for hover/active state changes (defers\n * to native button transition when unset).\n * @cssprop [--rc-fab-bg-hover] - Hover background (defers to native `:hover` styling when\n * unset).\n * @cssprop [--rc-fab-shadow-hover] - Hover shadow (defers to native `:hover` styling when\n * unset).\n * @cssprop [--rc-fab-shadow-active] - Pressed shadow (defers to native `:active` styling when\n * unset).\n * @cssprop [--rc-fab-active-transform] - Transform applied while pressed, e.g. `scale(0.96)`\n * (defers to native `:active` styling when unset).\n * @cssprop [--rc-fab-focus-ring] - Focus ring style (defers to native `:focus-visible` styling\n * when unset).\n * @cssprop [--rc-fab-focus-ring-offset] - Focus ring offset (defers to native `:focus-visible`\n * styling when unset).\n * @cssprop [--rc-fab-disabled-opacity] - Opacity applied when the button is disabled (defers to\n * native `:disabled` styling when unset).\n * @cssprop [--rc-fab-disabled-shadow] - Shadow applied when the button is disabled (defers to\n * native `:disabled` styling when unset).\n * @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.\n * @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.\n */\nexport class RCFab extends LitElement {\n static override styles = fabStyles;\n\n private _scrollCtrl?: ScrollObserverController;\n\n /** Viewport corner where the FAB is anchored. Uses logical inline/block directions. */\n @property({ type: String, reflect: true })\n position: 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start' = 'bottom-end';\n\n /** Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers. */\n @property({ type: Boolean, attribute: 'scroll-reveal', reflect: true })\n scrollReveal = false;\n\n constructor() {\n super();\n\n new NativeChildController<HTMLButtonElement>(this, {\n selector: ':scope > button',\n observe: true,\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > button',\n message:\n '[rc-fab] No direct child <button> found. Place a native <button> inside <rc-fab>.',\n });\n }\n },\n });\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n if (changed.has('scrollReveal') || (this.scrollReveal && !this._scrollCtrl)) {\n this._syncScrollFallback();\n }\n }\n\n private _syncScrollFallback(): void {\n if (this._scrollCtrl) {\n this._scrollCtrl.setOptions({ disabled: true });\n this.removeController(this._scrollCtrl);\n this._scrollCtrl = undefined;\n\n this.removeAttribute('scroll-below-threshold');\n }\n\n if (!this.scrollReveal || CSS.supports('animation-timeline: scroll()')) {\n return;\n }\n\n const threshold = this._getThreshold();\n\n this._scrollCtrl = new ScrollObserverController(this, {\n target: () => findNearestScrollAncestor(this),\n threshold,\n onScroll: (scrollTop) => {\n this.toggleAttribute('scroll-below-threshold', scrollTop < threshold);\n },\n });\n }\n\n private _getThreshold(): number {\n const raw = getComputedStyle(this).getPropertyValue('--rc-fab-scroll-threshold').trim();\n const n = parseFloat(raw);\n\n return Number.isFinite(n) ? n : 300;\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n\nexport default RCFab;\n"],"names":["fabStyles","css","_RCFab","LitElement","NativeChildController","changed","threshold","ScrollObserverController","findNearestScrollAncestor","scrollTop","raw","n","html","RCFab","__decorateClass","property"],"mappings":";;;AAEO,MAAMA,IAAYC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACwHlB,MAAMC,IAAN,MAAMA,UAAcC,EAAW;AAAA,EAapC,cAAc;AACZ,UAAA,GAPF,KAAA,WAAoE,cAIpE,KAAA,eAAe,IAKb,IAAIC,EAAyC,MAAM;AAAA,MACjD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,MAAM;AAAA,MAQjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEmB,QAAQC,GAA+B;AACxD,UAAM,QAAQA,CAAO,IAEjBA,EAAQ,IAAI,cAAc,KAAM,KAAK,gBAAgB,CAAC,KAAK,gBAC7D,KAAK,oBAAA;AAAA,EAET;AAAA,EAEQ,sBAA4B;AASlC,QARI,KAAK,gBACP,KAAK,YAAY,WAAW,EAAE,UAAU,IAAM,GAC9C,KAAK,iBAAiB,KAAK,WAAW,GACtC,KAAK,cAAc,QAEnB,KAAK,gBAAgB,wBAAwB,IAG3C,CAAC,KAAK,gBAAgB,IAAI,SAAS,8BAA8B;AACnE;AAGF,UAAMC,IAAY,KAAK,cAAA;AAEvB,SAAK,cAAc,IAAIC,EAAyB,MAAM;AAAA,MACpD,QAAQ,MAAMC,EAA0B,IAAI;AAAA,MAC5C,WAAAF;AAAA,MACA,UAAU,CAACG,MAAc;AACvB,aAAK,gBAAgB,0BAA0BA,IAAYH,CAAS;AAAA,MACtE;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEQ,gBAAwB;AAC9B,UAAMI,IAAM,iBAAiB,IAAI,EAAE,iBAAiB,2BAA2B,EAAE,KAAA,GAC3EC,IAAI,WAAWD,CAAG;AAExB,WAAO,OAAO,SAASC,CAAC,IAAIA,IAAI;AAAA,EAClC;AAAA,EAEmB,SAAS;AAC1B,WAAOC;AAAA,EACT;AACF;AAxEEV,EAAgB,SAASF;AADpB,IAAMa,IAANX;AAOLY,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAN9BF,EAOX,WAAA,UAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB,SAAS,IAAM;AAAA,GAV3DF,EAWX,WAAA,cAAA;"}
@@ -1,4 +1,4 @@
1
- import { R as e } from "./rc-fab-CKowGgDZ.js";
1
+ import { R as e } from "./rc-fab-CdJ-peVY.js";
2
2
  customElements.get("rc-fab") || customElements.define("rc-fab", e);
3
3
  export {
4
4
  e as RCFab
package/dist/rc-fab.js CHANGED
@@ -1,4 +1,4 @@
1
- import { R as r } from "./rc-fab-CKowGgDZ.js";
1
+ import { R as r } from "./rc-fab-CdJ-peVY.js";
2
2
  export {
3
3
  r as RCFab
4
4
  };
@@ -50,31 +50,62 @@ declare global {
50
50
  * @slot default - The native `<button>` element. The button's own accessible
51
51
  * name (text content or `aria-label`) serves as the FAB's accessible name.
52
52
  *
53
+ * @attr position - Viewport corner where the FAB is anchored.
54
+ * @attr scroll-reveal - Reveal the FAB only after the page scrolls past
55
+ * `--rc-fab-scroll-threshold`.
56
+ * @attr [scroll-below-threshold] - Present when `scroll-reveal` is active and the page hasn't
57
+ * scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation
58
+ * fallback; unused when the browser supports `animation-timeline: scroll()`.
59
+ *
53
60
  * @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.
54
61
  * @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.
55
62
  * @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.
56
63
  * @cssprop [--rc-fab-z-index=10] - Stacking order.
57
- * @cssprop [--rc-fab-bg=ButtonFace] - Button background colour.
58
- * @cssprop [--rc-fab-bg-hover=var(--rc-fab-bg)] - Hover background colour.
59
- * @cssprop [--rc-fab-color=ButtonText] - Button foreground colour.
60
- * @cssprop [--rc-fab-size=3.5rem] - Height and minimum width.
61
- * @cssprop [--rc-fab-radius=9999px] - Border-radius. Default is pill-shaped. Override to `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.
62
- * @cssprop [--rc-fab-shadow=none] - Elevation shadow.
63
- * @cssprop [--rc-fab-shadow-hover=var(--rc-fab-shadow)] - Hover shadow.
64
- * @cssprop [--rc-fab-shadow-active=none] - Pressed shadow.
65
- * @cssprop [--rc-fab-padding-inline=1rem] - Inline padding.
66
- * @cssprop [--rc-fab-gap=0.5rem] - Gap between icon and label text.
67
- * @cssprop [--rc-fab-font-family=inherit] - Font family for label text.
68
- * @cssprop [--rc-fab-font-size=0.875rem] - Font size for label text.
69
- * @cssprop [--rc-fab-font-weight=500] - Font weight for label text.
70
- * @cssprop [--rc-fab-letter-spacing=0.00625em] - Letter spacing for label text.
71
- * @cssprop [--rc-fab-focus-ring=2px solid currentColor] - Focus ring style.
72
- * @cssprop [--rc-fab-focus-ring-offset=2px] - Focus ring offset.
73
- * @cssprop [--rc-fab-disabled-opacity=0.38] - Opacity applied when the button is disabled.
74
- * @cssprop [--rc-fab-transition-duration=200ms] - Transition speed for hover and active states.
64
+ * @cssprop [--rc-fab-gap] - Gap between icon and label text. Unset by default; packaged themes
65
+ * such as `rc-theme-material` set `0.5rem`.
66
+ * @cssprop [--rc-fab-size] - Minimum inline size and block size of the button. Unset by
67
+ * default (native button sizing applies); packaged themes such as `rc-theme-material` set
68
+ * `3.5rem`.
69
+ * @cssprop [--rc-fab-padding-block] - Button block-axis padding (defers to native button
70
+ * padding when unset).
71
+ * @cssprop [--rc-fab-padding-inline] - Button inline-axis padding (defers to native button
72
+ * padding when unset).
73
+ * @cssprop [--rc-fab-bg] - Button background (defers to native button background when unset).
74
+ * @cssprop [--rc-fab-color] - Button foreground color (defers to native button color when
75
+ * unset).
76
+ * @cssprop [--rc-fab-border] - Button border (defers to native button border when unset).
77
+ * @cssprop [--rc-fab-radius] - Button border-radius (defers to native button radius when
78
+ * unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for
79
+ * Material rounded-square, etc.
80
+ * @cssprop [--rc-fab-shadow] - Elevation shadow (defers to native button shadow when unset).
81
+ * @cssprop [--rc-fab-font-family] - Font family for label text (defers to native button font
82
+ * when unset).
83
+ * @cssprop [--rc-fab-font-size] - Font size for label text (defers to native button font when
84
+ * unset).
85
+ * @cssprop [--rc-fab-font-weight] - Font weight for label text (defers to native button font
86
+ * when unset).
87
+ * @cssprop [--rc-fab-letter-spacing] - Letter spacing for label text (defers to native button
88
+ * styling when unset).
89
+ * @cssprop [--rc-fab-transition] - Transition shorthand for hover/active state changes (defers
90
+ * to native button transition when unset).
91
+ * @cssprop [--rc-fab-bg-hover] - Hover background (defers to native `:hover` styling when
92
+ * unset).
93
+ * @cssprop [--rc-fab-shadow-hover] - Hover shadow (defers to native `:hover` styling when
94
+ * unset).
95
+ * @cssprop [--rc-fab-shadow-active] - Pressed shadow (defers to native `:active` styling when
96
+ * unset).
97
+ * @cssprop [--rc-fab-active-transform] - Transform applied while pressed, e.g. `scale(0.96)`
98
+ * (defers to native `:active` styling when unset).
99
+ * @cssprop [--rc-fab-focus-ring] - Focus ring style (defers to native `:focus-visible` styling
100
+ * when unset).
101
+ * @cssprop [--rc-fab-focus-ring-offset] - Focus ring offset (defers to native `:focus-visible`
102
+ * styling when unset).
103
+ * @cssprop [--rc-fab-disabled-opacity] - Opacity applied when the button is disabled (defers to
104
+ * native `:disabled` styling when unset).
105
+ * @cssprop [--rc-fab-disabled-shadow] - Shadow applied when the button is disabled (defers to
106
+ * native `:disabled` styling when unset).
75
107
  * @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.
76
108
  * @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.
77
- *
78
109
  */
79
110
  export declare class RCFab extends LitElement {
80
111
  static styles: import('lit').CSSResult;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.2",
6
+ "version": "0.4.1",
7
7
  "description": "Sticky floating action button modeled after the Material 3 Floating action button, wrapping a consumer-supplied button with scroll-aware visibility.",
8
8
  "repository": {
9
9
  "type": "git",
@@ -45,7 +45,7 @@
45
45
  "test:browser:firefox": "vitest --run --project=firefox"
46
46
  },
47
47
  "dependencies": {
48
- "@rcarls/rc-common": "workspace:*"
48
+ "@rcarls/rc-common": "0.4.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@custom-elements-manifest/analyzer": "0.11.0",
@@ -62,4 +62,4 @@
62
62
  "peerDependencies": {
63
63
  "lit": "^3.0.0"
64
64
  }
65
- }
65
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"rc-fab-CKowGgDZ.js","sources":["../src/rc-fab.styles.ts","../src/rc-fab.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const fabStyles = css`\n :host {\n position: var(--rc-fab-position, fixed);\n inset-block-end: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: var(--rc-fab-inset-inline, 1.5rem);\n z-index: var(--rc-fab-z-index, 10);\n display: inline-flex;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([position='bottom-start']) {\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n :host([position='top-end']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n }\n\n :host([position='top-start']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n ::slotted(button) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-fab-gap, 0.5rem);\n\n min-width: var(--rc-fab-size, 3.5rem);\n height: var(--rc-fab-size, 3.5rem);\n padding-block: 0;\n padding-inline: var(--rc-fab-padding-inline, 1rem);\n\n background: var(--rc-fab-bg, ButtonFace);\n color: var(--rc-fab-color, ButtonText);\n\n border: none;\n border-radius: var(--rc-fab-radius, 9999px);\n box-shadow: var(--rc-fab-shadow, var(--rc-shadow-level2, none));\n\n font-family: var(--rc-fab-font-family, var(--rc-font-family, inherit));\n font-size: var(--rc-fab-font-size, var(--rc-font-size, 0.875rem));\n font-weight: var(--rc-fab-font-weight, 500);\n letter-spacing: var(--rc-fab-letter-spacing, 0.00625em);\n white-space: nowrap;\n\n cursor: pointer;\n transition:\n background var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,\n box-shadow var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,\n transform var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease;\n }\n\n :host(:has(button:hover)) ::slotted(button) {\n background: var(--rc-fab-bg-hover, var(--rc-fab-bg, ButtonFace));\n box-shadow: var(--rc-fab-shadow-hover, var(--rc-fab-shadow, var(--rc-shadow-level3, none)));\n }\n\n :host(:has(button:active)) ::slotted(button) {\n box-shadow: var(--rc-fab-shadow-active, none);\n transform: scale(0.96);\n }\n\n :host(:has(button:focus-visible)) ::slotted(button) {\n outline: var(--rc-fab-focus-ring, var(--rc-focus-ring, 2px solid currentColor));\n outline-offset: var(--rc-fab-focus-ring-offset, var(--rc-focus-ring-offset, 2px));\n }\n\n :host(:has(button:disabled)) ::slotted(button) {\n opacity: var(--rc-fab-disabled-opacity, var(--rc-disabled-opacity, 0.38));\n pointer-events: none;\n box-shadow: none;\n }\n\n @keyframes rc-fab-scroll-reveal {\n from {\n opacity: 0;\n visibility: hidden;\n }\n 50% {\n visibility: visible;\n }\n to {\n opacity: 1;\n visibility: visible;\n }\n }\n\n @supports (animation-timeline: scroll()) {\n :host([scroll-reveal]) {\n /*\n * Override for embedded/demo contexts where root does not scroll:\n * style=\"--rc-fab-scroll-timeline: scroll(nearest block)\"\n */\n --rc-fab-scroll-timeline: scroll(root block);\n\n animation-name: rc-fab-scroll-reveal;\n animation-duration: 1ms; /* required; scroll position drives progress, not time */\n animation-timing-function: linear;\n animation-fill-mode: both;\n animation-timeline: var(--rc-fab-scroll-timeline);\n animation-range: calc(var(--rc-fab-scroll-threshold, 300px) - 100px)\n var(--rc-fab-scroll-threshold, 300px);\n }\n\n /* Keyboard escape hatch: always visible when the button has focus */\n :host([scroll-reveal]):has(button:focus-visible) {\n animation: none;\n opacity: 1;\n visibility: visible;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]) {\n animation-range: var(--rc-fab-scroll-threshold, 300px) var(--rc-fab-scroll-threshold, 300px);\n }\n }\n }\n\n /*\n * JS fallback for browsers without scroll-driven animation support.\n * [scroll-below-threshold] is toggled by ScrollObserverController;\n * transitions replicate the CSS animation's show/hide behavior.\n */\n @supports not (animation-timeline: scroll()) {\n /* visible state: visibility snaps immediately, opacity fades in */\n :host([scroll-reveal]) {\n transition:\n opacity 200ms linear,\n visibility 0s linear;\n }\n\n /* hidden state: opacity fades first, visibility snaps off after delay */\n :host([scroll-reveal][scroll-below-threshold]) {\n opacity: 0;\n visibility: hidden;\n transition:\n opacity 200ms linear,\n visibility 0s linear 200ms;\n }\n\n /* Keyboard escape hatch */\n :host([scroll-reveal][scroll-below-threshold]):has(button:focus-visible) {\n opacity: 1;\n visibility: visible;\n transition: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]),\n :host([scroll-reveal][scroll-below-threshold]) {\n transition: none;\n }\n }\n }\n`;\n\nexport default fabStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport {\n NativeChildController,\n ScrollObserverController,\n findNearestScrollAncestor,\n warnMissingDirectChild,\n} from '@rcarls/rc-common';\n\nimport fabStyles from './rc-fab.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-fab': RCFab;\n }\n}\n\n/**\n * Sticky floating action button modeled after the Material 3 Floating action button,\n * wrapping a consumer-supplied button with scroll-aware visibility.\n *\n * Adapted from the Material Design FAB component, use this for \"back to top\",\n * sticky CTAs, chat launchers, and of course as FABs in your Material Design PWA.\n *\n * Place a native `<button>` as the direct child. The button's own accessible\n * name (text content or `aria-label`) becomes the FAB's accessible name.\n *\n * Icons go inside the button alongside or instead of visible text.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-fab rc-fab docs}\n * @see {@link https://m3.material.io/components/floating-action-button/overview Material 3 Floating action button}\n *\n * @example Back to top (scroll-triggered)\n * ```html\n * <rc-fab scroll-reveal>\n * <button type=\"button\" aria-label=\"Back to top\" onclick=\"scrollTo({top:0,behavior:'smooth'})\">\n * <span aria-hidden=\"true\">↑</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Icon-only FAB (always visible)\n * ```html\n * <rc-fab>\n * <button type=\"button\" aria-label=\"Create\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">add</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Extended FAB (icon + visible label)\n * ```html\n * <rc-fab>\n * <button type=\"button\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">edit</span>\n * Compose\n * </button>\n * </rc-fab>\n * ```\n *\n * @slot default - The native `<button>` element. The button's own accessible\n * name (text content or `aria-label`) serves as the FAB's accessible name.\n *\n * @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.\n * @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.\n * @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.\n * @cssprop [--rc-fab-z-index=10] - Stacking order.\n * @cssprop [--rc-fab-bg=ButtonFace] - Button background colour.\n * @cssprop [--rc-fab-bg-hover=var(--rc-fab-bg)] - Hover background colour.\n * @cssprop [--rc-fab-color=ButtonText] - Button foreground colour.\n * @cssprop [--rc-fab-size=3.5rem] - Height and minimum width.\n * @cssprop [--rc-fab-radius=9999px] - Border-radius. Default is pill-shaped. Override to `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.\n * @cssprop [--rc-fab-shadow=none] - Elevation shadow.\n * @cssprop [--rc-fab-shadow-hover=var(--rc-fab-shadow)] - Hover shadow.\n * @cssprop [--rc-fab-shadow-active=none] - Pressed shadow.\n * @cssprop [--rc-fab-padding-inline=1rem] - Inline padding.\n * @cssprop [--rc-fab-gap=0.5rem] - Gap between icon and label text.\n * @cssprop [--rc-fab-font-family=inherit] - Font family for label text.\n * @cssprop [--rc-fab-font-size=0.875rem] - Font size for label text.\n * @cssprop [--rc-fab-font-weight=500] - Font weight for label text.\n * @cssprop [--rc-fab-letter-spacing=0.00625em] - Letter spacing for label text.\n * @cssprop [--rc-fab-focus-ring=2px solid currentColor] - Focus ring style.\n * @cssprop [--rc-fab-focus-ring-offset=2px] - Focus ring offset.\n * @cssprop [--rc-fab-disabled-opacity=0.38] - Opacity applied when the button is disabled.\n * @cssprop [--rc-fab-transition-duration=200ms] - Transition speed for hover and active states.\n * @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.\n * @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.\n *\n */\nexport class RCFab extends LitElement {\n static override styles = fabStyles;\n\n private _scrollCtrl?: ScrollObserverController;\n\n /** Viewport corner where the FAB is anchored. Uses logical inline/block directions. */\n @property({ type: String, reflect: true })\n position: 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start' = 'bottom-end';\n\n /** Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers. */\n @property({ type: Boolean, attribute: 'scroll-reveal', reflect: true })\n scrollReveal = false;\n\n constructor() {\n super();\n\n new NativeChildController<HTMLButtonElement>(this, {\n selector: ':scope > button',\n observe: true,\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > button',\n message:\n '[rc-fab] No direct child <button> found. Place a native <button> inside <rc-fab>.',\n });\n }\n },\n });\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n if (changed.has('scrollReveal') || (this.scrollReveal && !this._scrollCtrl)) {\n this._syncScrollFallback();\n }\n }\n\n private _syncScrollFallback(): void {\n if (this._scrollCtrl) {\n this._scrollCtrl.setOptions({ disabled: true });\n this.removeController(this._scrollCtrl);\n this._scrollCtrl = undefined;\n\n this.removeAttribute('scroll-below-threshold');\n }\n\n if (!this.scrollReveal || CSS.supports('animation-timeline: scroll()')) return;\n\n const threshold = this._getThreshold();\n\n this._scrollCtrl = new ScrollObserverController(this, {\n target: () => findNearestScrollAncestor(this),\n threshold,\n onScroll: (scrollTop) => {\n this.toggleAttribute('scroll-below-threshold', scrollTop < threshold);\n },\n });\n }\n\n private _getThreshold(): number {\n const raw = getComputedStyle(this).getPropertyValue('--rc-fab-scroll-threshold').trim();\n const n = parseFloat(raw);\n\n return Number.isFinite(n) ? n : 300;\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n\nexport default RCFab;\n"],"names":["fabStyles","css","_RCFab","LitElement","NativeChildController","changed","threshold","ScrollObserverController","findNearestScrollAncestor","scrollTop","raw","n","html","RCFab","__decorateClass","property"],"mappings":";;;AAEO,MAAMA,IAAYC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACyFlB,MAAMC,IAAN,MAAMA,UAAcC,EAAW;AAAA,EAapC,cAAc;AACZ,UAAA,GAPF,KAAA,WAAoE,cAIpE,KAAA,eAAe,IAKb,IAAIC,EAAyC,MAAM;AAAA,MACjD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,MAAM;AAAA,MAQjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEmB,QAAQC,GAA+B;AACxD,UAAM,QAAQA,CAAO,IAEjBA,EAAQ,IAAI,cAAc,KAAM,KAAK,gBAAgB,CAAC,KAAK,gBAC7D,KAAK,oBAAA;AAAA,EAET;AAAA,EAEQ,sBAA4B;AASlC,QARI,KAAK,gBACP,KAAK,YAAY,WAAW,EAAE,UAAU,IAAM,GAC9C,KAAK,iBAAiB,KAAK,WAAW,GACtC,KAAK,cAAc,QAEnB,KAAK,gBAAgB,wBAAwB,IAG3C,CAAC,KAAK,gBAAgB,IAAI,SAAS,8BAA8B,EAAG;AAExE,UAAMC,IAAY,KAAK,cAAA;AAEvB,SAAK,cAAc,IAAIC,EAAyB,MAAM;AAAA,MACpD,QAAQ,MAAMC,EAA0B,IAAI;AAAA,MAC5C,WAAAF;AAAA,MACA,UAAU,CAACG,MAAc;AACvB,aAAK,gBAAgB,0BAA0BA,IAAYH,CAAS;AAAA,MACtE;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEQ,gBAAwB;AAC9B,UAAMI,IAAM,iBAAiB,IAAI,EAAE,iBAAiB,2BAA2B,EAAE,KAAA,GAC3EC,IAAI,WAAWD,CAAG;AAExB,WAAO,OAAO,SAASC,CAAC,IAAIA,IAAI;AAAA,EAClC;AAAA,EAEmB,SAAS;AAC1B,WAAOC;AAAA,EACT;AACF;AAtEEV,EAAgB,SAASF;AADpB,IAAMa,IAANX;AAOLY,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAN9BF,EAOX,WAAA,UAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB,SAAS,IAAM;AAAA,GAV3DF,EAWX,WAAA,cAAA;"}