Previews

No matching results.

Pages

No matching results.

x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<button type="button" class="btn avatar" popovertarget="avatar_dropdown" aria-haspopup="true" aria-label="Open account menu">
<img alt="CN" src="/assets/cartoon-93de1535.jpg" />
</button>
<div popover id="avatar_dropdown" class="popover popover--block-end-start" style="--popover-size: 8rem;" role="menu" aria-label="Account">
<div class="menu" data-controller="menu" data-action="keydown->menu#navigate">
<div class="menu__group" role="group">
<a class="btn menu__item" data-menu-target="item" role="menuitem" href="#">Profile</a>
<a class="btn menu__item" data-menu-target="item" role="menuitem" href="#">Billing</a>
<a class="btn menu__item" data-menu-target="item" role="menuitem" href="#">Settings</a>
</div>
<hr class="menu__separator" role="separator"/>
<div class="menu__group" role="group">
<a class="btn menu__item text-negative" data-menu-target="item" role="menuitem" href="#">Log out</a>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<button type="button" class="btn avatar" popovertarget="avatar_dropdown" aria-haspopup="true" aria-label="Open account menu">
<%= image_tag "cartoon.jpg", alt: "CN" %>
</button>
<div popover id="avatar_dropdown" class="popover popover--block-end-start" style="--popover-size: 8rem;" role="menu" aria-label="Account">
<div class="menu" data-controller="menu" data-action="keydown->menu#navigate">
<div class="menu__group" role="group">
<%= link_to "Profile", "#", class: "btn menu__item", data: { menu_target: "item" }, role: "menuitem" %>
<%= link_to "Billing", "#", class: "btn menu__item", data: { menu_target: "item" }, role: "menuitem" %>
<%= link_to "Settings", "#", class: "btn menu__item", data: { menu_target: "item" }, role: "menuitem" %>
</div>
<hr class="menu__separator" role="separator"/>
<div class="menu__group" role="group">
<%= link_to "Log out", "#", class: "btn menu__item text-negative", data: { menu_target: "item" }, role: "menuitem" %>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
.avatar {
--avatar-badge-size: calc(var(--avatar-size) * .3125);
--avatar-box-shadow: 0 0 0 2px var(--color-bg);
--avatar-radius: var(--rounded-full);
--avatar-size: var(--size-8);
block-size: var(--avatar-size);
border-radius: var(--avatar-radius);
display: flex;
flex-shrink: 0;
inline-size: var(--avatar-size);
position: relative;
&.btn {
--btn-inline-size: var(--avatar-size);
--btn-padding: 0;
--btn-radius: var(--avatar-radius);
}
&.btn:hover {
filter: var(--brightness-90);
}
img {
aspect-ratio: var(--aspect-square);
block-size: var(--size-full);
border-radius: var(--avatar-radius);
inline-size: var(--size-full);
object-fit: cover;
}
span[role="img"] {
align-items: center;
background-color: var(--color-border-light);
block-size: var(--size-full);
border-radius: var(--avatar-radius);
color: var(--color-text-subtle);
display: flex;
font-size: calc(var(--avatar-size) * .5);
inline-size: var(--size-full);
justify-content: center;
user-select: none;
}
}
.avatar--sm {
--avatar-size: var(--size-6);
}
.avatar--lg {
--avatar-size: var(--size-10);
}
.avatar__badge {
background-color: var(--avatar-badge-color, var(--color-primary));
block-size: var(--avatar-badge-size);
border-radius: var(--rounded-full);
box-shadow: var(--avatar-box-shadow);
inline-size: var(--avatar-badge-size);
inset: auto 0 0 auto;
position: absolute;
&:has(.icon) {
align-items: center;
color: var(--color-text-reversed);
display: flex;
justify-content: center;
}
.icon {
--icon-size: calc(var(--avatar-badge-size) * .75);
}
}
.avatar-group {
align-items: center;
display: flex;
> .avatar {
box-shadow: var(--avatar-box-shadow);
}
> :not(:last-child) {
margin-inline-end: calc(var(--size-2) * -1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "item" ]
static values = { index: Number }
#observer
initialize() {
this.#observer = new IntersectionObserver(this.#reset.bind(this))
}
connect() {
this.#observer.observe(this.element)
}
disconnect() {
this.#observer.disconnect()
}
navigate(event) {
switch (event.key) {
case " ":
case "Enter":
this.#cancel(event)
this.#activate(event.target)
break
case "ArrowUp":
this.#cancel(event)
this.#prev()
break
case "ArrowDown":
this.#cancel(event)
this.#next()
break
case "Home":
this.#cancel(event)
this.#first()
break
case "End":
this.#cancel(event)
this.#last()
break
}
}
#cancel(event) {
event.stopPropagation()
event.preventDefault()
}
#activate(menuItem) {
menuItem.click()
}
#reset([ entry ]) {
entry.isIntersecting && this.#first()
}
#prev() {
if (this.indexValue > 0) {
this.indexValue--
this.#update()
}
}
#next() {
if (this.indexValue < this.#lastIndex) {
this.indexValue++
this.#update()
}
}
#first() {
this.indexValue = 0
this.#update()
}
#last() {
this.indexValue = this.#lastIndex
this.#update()
}
#update() {
this.#updateTabstops()
this.#focusItemAtIndex()
}
#updateTabstops() {
this.itemTargets.forEach((it, index) => {
it.tabIndex = index === this.indexValue ? 0 : -1
})
}
#focusItemAtIndex() {
this.itemTargets[this.indexValue].focus()
}
get #lastIndex() {
return this.itemTargets.length -1
}
}