1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+
4+ < head >
5+ < meta charset ="UTF-8 ">
6+ < meta http-equiv ="X-UA-Compatible " content ="IE=edge,chrome=1 ">
7+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
8+ < title > Focusgroup: Additional Concepts</ title >
9+ < link rel ="icon " type ="image/png " href ="https://edgestatic.azureedge.net/welcome/static/favicon.png ">
10+ < link rel ="stylesheet " href ="style.css ">
11+ </ head >
12+
13+ < body >
14+ < header class ="page-header ">
15+ < h1 > Additional Concepts</ h1 >
16+ < p > Nested focusgroups, opt-out, deep descendants, reading-flow, and
17+ feature detection.</ p >
18+ < nav > < a href ="index.html "> ← All Demos</ a > </ nav >
19+ </ header >
20+
21+ < main class ="page-content ">
22+
23+ <!-- ============================================================ -->
24+ <!-- Demo 1: Nested Focusgroups -->
25+ <!-- ============================================================ -->
26+ < section class ="demo-section ">
27+ < h2 > Nested Focusgroups</ h2 >
28+ < p >
29+ A nested focusgroup creates an independent navigation scope
30+ within an outer focusgroup.
31+ Each group has its own axis, wrapping, memory, and entry point.
32+ < kbd > Tab</ kbd > moves between groups;
33+ arrows navigate within.
34+ </ p >
35+ < div class ="attr-label "> focusgroup="toolbar inline" (outer) +
36+ focusgroup="toolbar inline wrap" (inner)</ div >
37+
38+ < div class ="try-it ">
39+ < strong > Try it:</ strong > Use < kbd > →</ kbd > in the outer toolbar.
40+ When focus reaches the nested group, press < kbd > Tab</ kbd >
41+ to enter it. Use arrows inside, then < kbd > Tab</ kbd > to exit
42+ back to the outer toolbar.
43+ </ div >
44+
45+ < div class ="demo-container ">
46+ < div focusgroup ="toolbar inline " aria-label ="Main toolbar " class ="toolbar-horizontal ">
47+ < button type ="button "> Save</ button >
48+ < button type ="button " focusgroupstart > Print</ button >
49+ < div focusgroup ="toolbar inline wrap " aria-label ="Text formatting " class ="nested-toolbar ">
50+ < button type ="button "> Bold</ button >
51+ < button type ="button "> Italic</ button >
52+ < button type ="button "> Underline</ button >
53+ </ div >
54+ < button type ="button "> Close</ button >
55+ < button type ="button "> Exit</ button >
56+ </ div >
57+ </ div >
58+
59+ < ul class ="notice-list ">
60+ < li > The nested toolbar is an < strong > independent scope</ strong >
61+ inside the outer toolbar</ li >
62+ < li > < kbd > Tab</ kbd > moves between the outer and inner
63+ focusgroups</ li >
64+ < li > Arrow keys navigate within whichever group currently has
65+ focus</ li >
66+ < li > Each group can have its own wrapping/memory/axis
67+ settings</ li >
68+ </ ul >
69+
70+ < details class ="source-code ">
71+ < summary > View source</ summary >
72+ < pre > < code > <div focusgroup="toolbar inline" aria-label="Main
73+ toolbar">
74+ <button type="button">Save</button>
75+ <button type="button" focusgroupstart>Print</button>
76+ <div focusgroup="toolbar inline wrap" aria-label="Text formatting">
77+ <button type="button">Bold</button>
78+ <button type="button">Italic</button>
79+ <button type="button">Underline</button>
80+ </div>
81+ <button type="button">Close</button>
82+ <button type="button">Exit</button>
83+ </div></ code > </ pre >
84+ </ details >
85+ </ section >
86+
87+ <!-- ============================================================ -->
88+ <!-- Demo 2: Opt-out with focusgroup="none" -->
89+ <!-- ============================================================ -->
90+ < section class ="demo-section ">
91+ < h2 > Opt-Out Segments with < code > focusgroup="none"</ code > </ h2 >
92+ < p >
93+ Use < code > focusgroup="none"</ code > to exclude specific elements
94+ from arrow navigation
95+ while keeping them tabbable. Arrow keys skip right over them.
96+ </ p >
97+ < div class ="attr-label "> focusgroup="toolbar inline"</ div >
98+
99+ < div class ="try-it ">
100+ < strong > Try it:</ strong > Use < kbd > →</ kbd > to navigate through
101+ the toolbar. Notice that "Help" and "Shortcuts" are
102+ < strong > skipped</ strong > by arrow keys but still reachable
103+ via < kbd > Tab</ kbd > .
104+ </ div >
105+
106+ < div class ="demo-container ">
107+ < div focusgroup ="toolbar inline " aria-label ="Segmented toolbar " class ="toolbar-horizontal ">
108+ < button type ="button "> New</ button >
109+ < button type ="button "> Open</ button >
110+ < button type ="button "> Save</ button >
111+ < span focusgroup ="none " style ="display: contents; ">
112+ < button type ="button " style ="opacity: 0.6; "> Help</ button >
113+ < button type ="button " style ="opacity: 0.6; "> Shortcuts</ button >
114+ </ span >
115+ < button type ="button "> Close</ button >
116+ < button type ="button "> Exit</ button >
117+ </ div >
118+ </ div >
119+
120+ < ul class ="notice-list ">
121+ < li > Arrow keys navigate: New → Open → Save → Close → Exit
122+ (skipping Help & Shortcuts)</ li >
123+ < li > < kbd > Tab</ kbd > still reaches Help and Shortcuts</ li >
124+ < li > The dimmed buttons visually indicate they're in a
125+ < code > focusgroup="none"</ code > zone
126+ </ li >
127+ </ ul >
128+
129+ < details class ="source-code ">
130+ < summary > View source</ summary >
131+ < pre > < code > <div focusgroup="toolbar inline"
132+ aria-label="Segmented toolbar">
133+ <button type="button">New</button>
134+ <button type="button">Open</button>
135+ <button type="button">Save</button>
136+ <span focusgroup="none">
137+ <button type="button">Help</button>
138+ <button type="button">Shortcuts</button>
139+ </span>
140+ <button type="button">Close</button>
141+ <button type="button">Exit</button>
142+ </div></ code > </ pre >
143+ </ details >
144+ </ section >
145+
146+ <!-- ============================================================ -->
147+ <!-- Demo 3: Deep Descendant Discovery -->
148+ <!-- ============================================================ -->
149+ < section class ="demo-section ">
150+ < h2 > Deep Descendant Discovery</ h2 >
151+ < p >
152+ Focusgroup items don't need to be direct children. The browser
153+ discovers focusable
154+ descendants at any depth (unless they are inside a nested
155+ focusgroup or < code > focusgroup="none"</ code > ).
156+ </ p >
157+ < div class ="attr-label "> focusgroup="toolbar inline"</ div >
158+
159+ < div class ="try-it ">
160+ < strong > Try it:</ strong > Use < kbd > →</ kbd > < kbd > ←</ kbd > — arrow
161+ navigation works even though buttons are deeply nested
162+ inside < code > <div></ code > and
163+ < code > <span></ code > wrappers.
164+ </ div >
165+
166+ < div class ="demo-container ">
167+ < div focusgroup ="toolbar inline " aria-label ="Nested wrappers " class ="toolbar-horizontal ">
168+ < div >
169+ < span > < button type ="button "> Alpha</ button > </ span >
170+ < span > < button type ="button "> Beta</ button > </ span >
171+ < span > < button type ="button "> Gamma</ button > </ span >
172+ </ div >
173+ </ div >
174+ </ div >
175+
176+ < ul class ="notice-list ">
177+ < li > Buttons are nested inside
178+ < code > <div><span></ code > wrappers
179+ </ li >
180+ < li > Focusgroup discovers them at any depth in the DOM tree</ li >
181+ < li > No flat list requirement — wrapper elements for styling are
182+ fine</ li >
183+ </ ul >
184+
185+ < details class ="source-code ">
186+ < summary > View source</ summary >
187+ < pre > < code > <div focusgroup="toolbar inline"
188+ aria-label="Nested wrappers">
189+ <div>
190+ <span><button
191+ type="button">Alpha</button></span>
192+ <span><button type="button">Beta</button></span>
193+ <span><button
194+ type="button">Gamma</button></span>
195+ </div>
196+ </div></ code > </ pre >
197+ </ details >
198+ </ section >
199+
200+ <!-- ============================================================ -->
201+ <!-- Demo 4: CSS reading-flow Integration -->
202+ <!-- ============================================================ -->
203+ < section class ="demo-section ">
204+ < h2 > CSS < code > reading-flow</ code > Integration</ h2 >
205+ < p >
206+ When CSS changes the visual order (e.g., < code > flex-direction:
207+ row-reverse</ code > ),
208+ < code > reading-flow: flex-visual</ code > tells the focusgroup to
209+ follow the < strong > visual</ strong >
210+ order rather than the DOM source order.
211+ </ p >
212+ < div class ="attr-label "> focusgroup="toolbar" + reading-flow:
213+ flex-visual</ div >
214+
215+ < div class ="try-it ">
216+ < strong > Try it:</ strong > Use < kbd > →</ kbd > to navigate. With
217+ < code > reading-flow: flex-visual</ code > , arrow navigation
218+ follows the visual left-to-right order (C → B → A), not the
219+ DOM order (A → B → C).
220+ </ div >
221+
222+ < div class ="demo-container ">
223+ < div focusgroup ="toolbar " aria-label ="Visual order " style ="display: flex; flex-direction: row-reverse;
224+ reading-flow: flex-visual; gap: 0.35rem; ">
225+ < button type ="button "> A (DOM first)</ button >
226+ < button type ="button "> B (DOM second)</ button >
227+ < button type ="button "> C (DOM third)</ button >
228+ </ div >
229+ </ div >
230+
231+ < ul class ="notice-list ">
232+ < li > DOM order: A, B, C — but < code > flex-direction:
233+ row-reverse</ code > visually renders C, B, A</ li >
234+ < li > < code > reading-flow: flex-visual</ code > tells focusgroup to
235+ follow visual order</ li >
236+ < li > Right arrow from C goes to B, then A (visual
237+ left-to-right)</ li >
238+ < li > Without < code > reading-flow</ code > , arrow keys would follow
239+ DOM order (A → B → C), which is visually backwards</ li >
240+ </ ul >
241+
242+ < details class ="source-code ">
243+ < summary > View source</ summary >
244+ < pre > < code > <div focusgroup="toolbar" aria-label="Visual
245+ order"
246+ style="display: flex; flex-direction: row-reverse;
247+ reading-flow: flex-visual;">
248+ <button type="button">A (DOM first)</button>
249+ <button type="button">B (DOM second)</button>
250+ <button type="button">C (DOM third)</button>
251+ </div></ code > </ pre >
252+ </ details >
253+ </ section >
254+
255+ <!-- ============================================================ -->
256+ <!-- Demo 5: Feature Detection -->
257+ <!-- ============================================================ -->
258+ < section class ="demo-section ">
259+ < h2 > Feature Detection</ h2 >
260+ < p >
261+ Check whether the browser supports < code > focusgroup</ code >
262+ before relying on it.
263+ If unsupported, you can fall back to a JavaScript-based roving
264+ tabindex or show a notice.
265+ </ p >
266+
267+ < div class ="demo-container ">
268+ < div id ="feature-detect-result " style ="padding: 0.75rem; border-radius: 4px; "> </ div >
269+ </ div >
270+
271+ < details class ="source-code ">
272+ < summary > View source</ summary >
273+ < pre > < code > <script>
274+ if ('focusgroup' in HTMLElement.prototype) {
275+ // focusgroup is supported — use it!
276+ console.log('focusgroup is supported');
277+ } else {
278+ // Not supported — fall back to JS roving tabindex
279+ console.log('focusgroup is NOT supported');
280+ }
281+ </script></ code > </ pre >
282+ </ details >
283+ </ section >
284+
285+ </ main >
286+
287+ < script src ="shared.js "> </ script >
288+ < script >
289+ // Live feature detection result
290+ ( function ( ) {
291+ var result = document . getElementById ( "feature-detect-result" ) ;
292+ if ( ! result ) return ;
293+ if ( "focusgroup" in HTMLElement . prototype ) {
294+ result . textContent = "✅ focusgroup IS supported in this
295+ browser . " ;
296+ result . style . background = "var(--accent-light)" ;
297+ result . style . color = "var(--success)" ;
298+ } else {
299+ result . textContent = "❌ focusgroup is NOT supported in this
300+ browser . Enable experimental flags to try these demos . ";
301+ result . style . background = "var(--warning-bg)" ;
302+ result . style . color = "var(--warning-text)" ;
303+ }
304+ } ) ( ) ;
305+ </ script >
306+ </ body >
307+
308+ </ html >
0 commit comments