@rcarls/rc-app-bar 0.1.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.
package/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # `@rcarls/rc-app-bar`
2
+
3
+ A headless app bar with leading, title, exact-center, and trailing regions.
4
+ It uses a single consumer-owned title element and supports pinned, continuously
5
+ collapsing, and direction-sensitive hide-on-scroll behavior.
6
+
7
+ Material 3 and Apple HIG inform the available structures and behaviors, but the
8
+ default appearance remains deliberately UA-like: system colors, a thin
9
+ scrolled divider, and no supplied action controls or decorative icons.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @rcarls/rc-app-bar
15
+ ```
16
+
17
+ ```js
18
+ import '@rcarls/rc-app-bar/define';
19
+ ```
20
+
21
+ ## Basic usage
22
+
23
+ ```html
24
+ <header>
25
+ <rc-app-bar scroll-target="#content">
26
+ <button slot="leading" aria-label="Back">&larr;</button>
27
+ <span>Inbox</span>
28
+ <rc-toolbar slot="trailing" label="Actions">...</rc-toolbar>
29
+ </rc-app-bar>
30
+ </header>
31
+ <div id="content" class="scroll-area">...</div>
32
+ ```
33
+
34
+ The default slot is the single title region. It may contain a title, subtitle,
35
+ or any other consumer markup. The title node remains connected and is never
36
+ duplicated while the bar changes layout.
37
+
38
+ ## Expanded and scroll behaviors
39
+
40
+ `variant="expanded"` places the title in a flexible second row. Its height
41
+ comes from the title content rather than a Material-specific fixed size.
42
+
43
+ ```html
44
+ <rc-app-bar
45
+ variant="expanded"
46
+ scroll-behavior="collapse"
47
+ scroll-target="#content"
48
+ >
49
+ <button slot="leading" aria-label="Back">&larr;</button>
50
+ <div>
51
+ <strong>Slow-Roasted Tomato Pasta</strong>
52
+ <small>Summer recipes</small>
53
+ </div>
54
+ <button slot="trailing" aria-label="Edit">Edit</button>
55
+ </rc-app-bar>
56
+ ```
57
+
58
+ Scroll behaviors are exclusive:
59
+
60
+ - `pinned` keeps the current structure visible and only exposes scrolled state.
61
+ - `collapse` continuously shrinks an expanded title row over its measured
62
+ height, then keeps the compact row visible.
63
+ - `hide` hides the complete bar while scrolling down past `scroll-threshold`
64
+ and reveals it when scrolling up or returning near the top.
65
+
66
+ The component never consumes, stops, or rewrites the document's scroll.
67
+
68
+ ## Exact-center composition
69
+
70
+ The optional `center` slot remains at the host's geometric midpoint even when
71
+ leading and trailing controls have different widths. This is useful for
72
+ desktop controls or an [`rc-search-bar`](../rc-search-bar/):
73
+
74
+ ```html
75
+ <rc-app-bar>
76
+ <button slot="leading">Back</button>
77
+ <span>Recipes</span>
78
+ <rc-search-bar slot="center" placeholder="Search recipes">
79
+ <input type="search" aria-label="Search recipes" />
80
+ </rc-search-bar>
81
+ <button slot="trailing" aria-label="Account">Account</button>
82
+ </rc-app-bar>
83
+ ```
84
+
85
+ Title and center content may coexist. The title is constrained before it can
86
+ overlap the centered content. Without center content, the title uses all space
87
+ between leading and trailing controls; consumers may apply
88
+ `rc-app-bar::part(title) { text-align: center; }`.
89
+
90
+ ## Controlled state
91
+
92
+ `scrolled` follows the library's controlled/uncontrolled convention:
93
+
94
+ - Set `scrollTarget` or `scroll-target` for uncontrolled observation.
95
+ - Assign `scrolled` to control the endpoint silently.
96
+ - Assign `undefined` to release control back to observation.
97
+
98
+ In controlled collapse mode, `true` snaps to compact and `false` restores the
99
+ expanded row. Controlled hide mode remains visible because a boolean value
100
+ cannot express scroll direction.
101
+
102
+ State output is available as `data-scrolled`, `data-collapsed`, and
103
+ `data-hidden`, with equivalent `:state(scrolled)`, `:state(collapsed)`, and
104
+ `:state(hidden)` custom states.
105
+
106
+ ## View Transitions
107
+
108
+ Document View Transitions are consumer-owned progressive enhancement. Assign a
109
+ unique `view-transition-name` to the light-DOM title element, not
110
+ `::part(title)` or the shadow-owned slot wrapper:
111
+
112
+ ```html
113
+ <rc-app-bar id="recipe-bar" variant="expanded" scroll-behavior="collapse">
114
+ <h1 class="recipe-title">Slow-Roasted Tomato Pasta</h1>
115
+ </rc-app-bar>
116
+ ```
117
+
118
+ ```css
119
+ .recipe-title {
120
+ view-transition-name: recipe-app-bar-title;
121
+ }
122
+
123
+ ::view-transition-old(recipe-app-bar-title) {
124
+ animation: 180ms ease-out both title-out;
125
+ }
126
+
127
+ ::view-transition-new(recipe-app-bar-title) {
128
+ animation: 180ms 120ms ease-in both title-in;
129
+ }
130
+
131
+ @keyframes title-out {
132
+ to {
133
+ opacity: 0;
134
+ translate: 0 -1rem;
135
+ }
136
+ }
137
+
138
+ @keyframes title-in {
139
+ from {
140
+ opacity: 0;
141
+ }
142
+ }
143
+ ```
144
+
145
+ ```js
146
+ const bar = document.querySelector('#recipe-bar');
147
+ const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)');
148
+
149
+ async function setCollapsed(collapsed) {
150
+ const update = async () => {
151
+ bar.scrolled = collapsed;
152
+ await bar.updateComplete;
153
+ };
154
+
155
+ if (!document.startViewTransition || reduceMotion.matches) {
156
+ await update();
157
+ return;
158
+ }
159
+
160
+ document.startViewTransition(update);
161
+ }
162
+ ```
163
+
164
+ The app bar never starts a document transition itself because the API is
165
+ global and may conflict with navigation or unrelated components. Unsupported
166
+ browsers receive the immediate endpoint change. Give concurrently rendered
167
+ titles unique transition names.
168
+
169
+ ## Styling
170
+
171
+ The component supplies no controls or action icons. The default scrolled
172
+ separator is the `scroll-shadow` part, despite being rendered as a system-color
173
+ border by default:
174
+
175
+ ```css
176
+ rc-app-bar::part(scroll-shadow) {
177
+ border-block-end: 0;
178
+ box-shadow: 0 1px 4px rgb(0 0 0 / 0.2);
179
+ }
180
+
181
+ rc-app-bar.glass::part(root) {
182
+ background: color-mix(in srgb, Canvas 70%, transparent);
183
+ backdrop-filter: blur(18px);
184
+ }
185
+ ```
186
+
187
+ ### CSS custom properties
188
+
189
+ | Property | Default | Description |
190
+ | --- | --- | --- |
191
+ | `--rc-app-bar-bg` | `Canvas` | Bar background |
192
+ | `--rc-app-bar-color` | `CanvasText` | Bar text color |
193
+ | `--rc-app-bar-compact-min-height` | `3rem` | Compact row minimum height |
194
+ | `--rc-app-bar-expanded-padding-block` | `0.75em` | Expanded title block padding |
195
+ | `--rc-app-bar-padding-inline` | `0.75em` | Horizontal padding |
196
+ | `--rc-app-bar-gap` | `0.5em` | Gap between regions |
197
+ | `--rc-app-bar-transition-duration` | `200ms` | Endpoint and hide transition duration |
198
+ | `--rc-app-bar-scroll-shadow` | `1px solid GrayText` | Scrolled separator border |
199
+
200
+ ### CSS parts
201
+
202
+ `root`, `leading`, `title`, `center`, `trailing`, `scroll-shadow`
203
+
204
+ ## Breaking migration
205
+
206
+ - Replace `variant="small"` with `variant="compact"`.
207
+ - Replace `variant="medium"` with `variant="expanded"`.
208
+ - Remove `slot="expanded-title"` and keep one title in the default slot.
209
+ - Replace old fixed height/title-font tokens and the `title-row`/`expanded`
210
+ parts with the structural API documented above.
211
+
212
+ ## Accessibility
213
+
214
+ - The host has no implicit landmark role. Wrap a page-level instance in
215
+ `<header>` when appropriate.
216
+ - Consumer-provided controls retain their native semantics and keyboard
217
+ behavior.
218
+ - The single title remains connected and exposed to assistive technology in
219
+ every visual state.
220
+ - Movement and fades are disabled under `prefers-reduced-motion: reduce`.
221
+ - The default divider uses `GrayText` and remains visible in forced colors.