Merge pull request #302 from basecamp/a11y-first-round

First round of accessibility fixes
This commit is contained in:
Bruno Prieto
2025-03-14 18:01:57 +01:00
committed by GitHub
28 changed files with 253 additions and 203 deletions
+1
View File
@@ -263,6 +263,7 @@
}
/* Accessibility */
.visually-hidden,
.for-screen-reader {
block-size: 1px;
clip-path: inset(50%);
+4 -2
View File
@@ -10,8 +10,10 @@ module AvatarsHelper
AVATAR_COLORS[Zlib.crc32(user.to_param) % AVATAR_COLORS.size]
end
def avatar_tag(user, **options)
link_to user_path(user), title: user.name, class: "btn avatar", data: { turbo_frame: "_top" } do
def avatar_tag(user, hidden_for_screen_reader: false, **options)
link_to user_path(user), title: user.name, class: "btn avatar", data: { turbo_frame: "_top" },
aria: { hidden: hidden_for_screen_reader, label: user.name },
tabindex: hidden_for_screen_reader ? -1 : nil do
avatar_image_tag(user, **options)
end
end
+1
View File
@@ -3,6 +3,7 @@ module MessagesHelper
turbo_frame_tag dom_id(bubble, :messages),
class: "comments align-center center borderless margin flex flex-column gap-half",
style: "--bubble-color: <%= bubble.color %>",
role: "group", aria: { label: "Messages" },
data: {
controller: "created-by-current-user",
created_by_current_user_mine_class: "comment--mine"
@@ -6,14 +6,15 @@ export default class extends Controller {
static values = { reacterId: Number }
connect() {
if (this.#currentUserIsreacter) {
if (this.#currentUserIsReacter) {
this.#setAccessibleAttributes()
}
}
reveal() {
if (this.#currentUserIsreacter) {
if (this.#currentUserIsReacter) {
this.element.classList.toggle(this.revealClass)
this.contentTarget.ariaExpanded = this.element.classList.contains(this.revealClass)
this.buttonTarget.focus()
}
}
@@ -23,11 +24,12 @@ export default class extends Controller {
}
#setAccessibleAttributes() {
this.contentTarget.setAttribute('tabindex', '0')
this.contentTarget.setAttribute('aria-describedby', 'delete_reaction_accessible_label')
this.contentTarget.role = "button"
this.contentTarget.tabIndex = 0
this.contentTarget.ariaExpanded = false
}
get #currentUserIsreacter() {
get #currentUserIsReacter() {
return Current.user.id === this.reacterIdValue
}
}
+3 -2
View File
@@ -1,4 +1,5 @@
<%= tag.div class: "bubble__detail bubble__boosts",
role: "group", aria: { label: "Boosts" },
data: {
controller: "animation toggle-class",
animation_play_class: "boosting",
@@ -8,7 +9,7 @@
<%= form_with url: bucket_bubble_boosts_path(bubble.bucket, bubble),
class: "boost__form",
data: { action: "toggle-class#toggle" } do %>
<%= text_field_tag :boost_count, bubble.boosts_count, min: 1, class: "input borderless boost__input", autocomplete: "off" %>
<%= tag.button "+", class: "btn btn--plain boost__btn", type: :submit %>
<%= text_field_tag :boost_count, bubble.boosts_count, min: 1, class: "input borderless boost__input", autocomplete: "off", aria: { label: "Boost count" } %>
<%= tag.button "+", class: "btn btn--plain boost__btn", type: :submit, aria: { label: "Boost" } %>
<% end %>
<% end %>
+2 -2
View File
@@ -1,8 +1,8 @@
<% bubble.assignees.each do |assignee| %>
<article class="bubble__detail bubble__assignee">
<div class="btn btn--plain position-relative fill-transparent">
<span class="assignee__name"><span><%= assignee.name %></span></span>
<%= avatar_image_tag assignee, loading: :lazy, class: "avatar flex-item-no-shrink" %>
<span class="assignee__name" aria-hidden="true"><span><%= assignee.name %></span></span>
<%= avatar_image_tag assignee, loading: :lazy, class: "avatar flex-item-no-shrink", aria: { hidden: true } %>
<span class="for-screen-reader">Assigned to <%= assignee.name %></span>
</div>
</article>
+21 -16
View File
@@ -1,19 +1,22 @@
<% cache bubble do %>
<div class="<%= class_names("bubble", drafted: bubble.drafted?, popped: bubble.popped?) %>"
style="view-transition-name: bubble-<%= bubble.id -%>; --bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
data-bubble-size-target="bubble"
data-activity-score="<%= bubble.activity_score %>"
data-activity-score-at="<%= bubble.activity_score_at.to_i %>"
data-controller="animation upload-preview"
data-animation-play-class="bubble--wobble"
data-animation-play-on-load-value="true"
data-action="mouseover->animation#play">
<%= tag.div class: [ "bubble", drafted: bubble.drafted?, popped: bubble.popped? ],
style: "view-transition-name: bubble-#{bubble.id}; --bubble-color: #{bubble.color}; #{bubble_rotation(bubble)}",
role: "group", aria: { label: bubble.title },
data: {
bubble_size_target: "bubble",
activity_score: bubble.activity_score,
activity_score_at: bubble.activity_score_at.to_i,
controller: "animation upload-preview",
animation_play_class: "bubble--wobble",
animation_play_on_load_value: true,
action: "mouseover->animation#play" } do %>
<div>
<h1 class="bubble__title">
<h1 class="bubble__title" role="presentation">
<% if bubble.published? %>
<%= turbo_frame_tag bubble, :edit do %>
<%= link_to bubble_title(bubble), edit_bucket_bubble_path(bubble.bucket, bubble), class: "txt-undecorated bubble__title-rendered" %>
<%= link_to bubble_title(bubble), edit_bucket_bubble_path(bubble.bucket, bubble),
class: "txt-undecorated bubble__title-rendered",
role: "button", aria: { label: "Edit name: #{bubble.title}" } %>
<% end %>
<% else %>
<%= form_with model: bubble, url: bucket_bubble_path(bubble.bucket, bubble), id: "bubble_form", data: { controller: "auto-save" } do |form| %>
@@ -30,14 +33,16 @@
<div class="bubble__shape"></div>
<svg class="bubble__splat" viewBox="0 0 934 914" xmlns="http://www.w3.org/2000/svg"><circle cx="485.2" cy="467.2" r="286.5"/><path d="m197.7 219.1c-.2-7.1 5.8-11.7 12.7-13.2 34-7.1 53.5 29.2 81.4 40 39.5 6.4 25.9-54 30.8-77.7 17.2-65.3 78.4 67.2 108.2 55.7 26.8-17.3 25.9-58.8 38.7-86.1 9.6-41.5 30.4-2.6 48.6 10.1 14.2 11.2 31.9 17.9 45.1 29.9 13 11.2 20 29.1 35.8 36.8 20.2 9.1 44.8 2.3 65.2 11.8 18.3 7.2 33.8 22.6 52.5 30.4 23 8.6 42.4 1.8 37.8 35.1 2.7 36.8 38.3 71.9 40.1 108.6-.2 21.1-30.1 21.7-22.4 36.3 22.1 32.5-7.7 51.1 21 93.3 7 43.2-91.2-7.2-14.3 78.2 13.2 13.7 22.3 28.2 4.5 40.2-18.7 18.4-19.5 18.4-45 13.8-15.5-1.2 1.8 16.5-4.6 22.7-21.1 8-6.7 22-14.6 34.9-6 3.2-14.7-2.6-21.1-4-17.2-6.2-11.5 17.5-15.3 25.7-4 7.3-15.6 1.5-22.2-.2-26.7-10.8-55.1-3.1-82.4-.2-14.2 1.1-26 2.2-30.2 18.3-8 20.8-7.5 87.5-41.5 68.5-15.3-7.2-29.8-51.7-45.3-26.4-2.8 3.9-6.4 9.3-11.2 5.9-20.7-27.6-17-71-54.3-25.6-29.6 29.4-32.5 2.5-48.8-20.5-18.9-17.1-48.4-20.5-61.6-44.6-18.5-27.6-35.8-54.6-60.2-77.9-10.5-10.8-60.9-17.2-34.7-35.6 42.5-23.5-50.6-14-19.6-30.8 33.5-11.7 12.7-23.2-10.3-28.1-6.2-1.9-22-3.3-22.7-8.2 2.9-11.2 13.8-26.5 22.6-35.7 38.3-20.1-7-33.5-23.6-48.2-4-4.4 4.8-2.9 5.5-5.8-7.4-11.8-11.8-10-.4-21.8 3.2-7.8-4.9-17.6-3.8-25.5 8.1-21 57.9-6.5 64.2-36.2-3.5-26.1-16.4-50.3 3.5-75 5.9-8.8 22.4-16.8 19.3-28.7-5.7-14.7-26.4-23.7-27.6-40.2z"/><path d="m573 256.7s29.6-24.9 55-64.4 40.3-158 88.1-127.9c44.4 28-27.7 97.3-60.3 143.3-32.6 45.9-14.3 85.7-14.3 85.7l-68.5-36.6z"/><path d="m606.4 674.7s-11.3 37-11.2 84c0 47 51.9 154.6-4.6 155.3-52.5.6-29.6-96.8-27.1-153.1s-34.5-79.7-34.5-79.7z"/><path d="m325.5 622.7s-34 16.1-71.9 44c-37.8 27.9-104.9 118.2-126.8 89.7-20.3-26.5 66.8-72.5 113.1-104.6s51.2-64.9 51.2-64.9l34.4 35.9z"/><path d="m225.7 388.5s-30.8-35.1-75.8-72.7c-45.1-37.5-169.2-98.3-146.9-126.5 20.8-26.1 104.8 63 157.6 109.3 52.9 46.2 90.4 46.9 90.4 46.9l-25.4 43z"/><path d="m327.7 593.7s-47.6 1.4-103.9 17.7c-56.3 16.2-170.6 104.9-187.6 48.8-15.8-52.2 107.7-62.8 175.9-79.7s85.8-61.9 85.8-61.9l29.9 75.1z"/><path d="m443.5 344.2s0-46.7-8.9-104.6c-9-57.9-62-185.6-26.7-192 32.9-5.9 37 116.5 46.2 186.2 9.2 69.6 36.9 95 36.9 95l-47.5 15.5z"/><path d="m433.2 340.9s-6.8-37-19.5-82.8c-12.8-45.8-56.9-146.5-40.8-151.7 15-4.8 34.8 92 49.3 147s31.6 74.9 31.6 74.9l-20.6 12.5z"/><path d="m496.8 298.1s10.8-45.7 13.6-104.2c2.8-58.6-29.9-194.6 12.9-193.4 39.8 1.1 16.5 121.7 11.2 191.8-5.3 70 21.3 100.6 21.3 100.6l-59.1 5.2z"/><path d="m647.8 525s22.1 41.1 57.6 87.8c35.4 46.7 142.8 133.8 114.7 156.2-26.1 20.8-88-84.9-129.1-141.8-41.2-56.9-77.6-66-77.6-66z"/><path d="m658.4 523s23.5 29.3 56.5 63.5 119.7 101.9 108 114.1c-10.9 11.4-74.3-64.4-113.3-105.9s-63.4-50.9-63.4-50.9z"/><path d="m622.8 591s12.2 45.3 37.6 98.2 118.8 157 80.6 176.3c-35.6 17.9-72.4-99.3-101.1-163.4-28.6-64.1-66.6-78.4-66.6-78.4l49.5-32.6z"/><path d="m613.7 441.6s43.3 17.5 100.4 30.7c57.1 13.3 195.4 11.6 188.1 46.8-6.8 32.7-121.9-9.1-189.9-26.6s-101.9-1.2-101.9-1.2l3.3-49.8z"/><path d="m666.2 496.5s38.4 27.1 91.7 51.5 191.7 44.8 174.6 84.1c-15.9 36.5-119.1-30.1-182.2-61.1-63-31-101.3-17.7-101.3-17.7l17.2-56.7z"/><path d="m57.2 500.1c-7.1-1.3-14.4-2-21.5-.7-13.9 2.4-27.3 11.4-31.7 25.2-5 15.2 2.5 30.2 12.9 41.7 7.5 8.5 16.3 16.5 27.6 16.6 19.9-.7 36.3-19.9 37.6-39 1.6-18.3-8.2-36.2-27.2-40.2-3.5-.8-7.2-1.2-10.8-1.5"/><path d="m796.1 362.9c5.4 21.4 22.7 41 44.2 46.3 16.7 4.5 33.7-3 43.2-17.2 24.3-38.2-3-125.5-53.2-116.5-8 1.5-15.8 5.8-21.4 11.6-18.2 19.3-19.3 51.3-12.8 75.6z"/><path d="m255.1 782.9c5.4 21.4 22.7 41 44.2 46.3 16.7 4.5 33.7-3 43.2-17.2 24.3-38.2-3-125.5-53.2-116.5-8 1.5-15.8 5.8-21.4 11.6-18.2 19.3-19.3 51.3-12.8 75.6z"/><path d="m771 224.4c-3.4-.5-6.8-1-10.2-1.1-23.8-1.1-36.1 23.5-16.7 36.4 9.4 6.3 22.2 2.4 26.7-7.7 5.2-11.6-.7-20.3-10.6-26.9"/><path d="m402 812.4c-3.4-.5-6.8-1-10.2-1.1-23.8-1.1-36.1 23.5-16.7 36.4 9.4 6.3 22.2 2.4 26.7-7.7 5.2-11.6-.7-20.3-10.6-26.9"/></svg>
<%= link_to bucket_bubble_path(bubble.bucket, bubble), class: "bubble__link" do %>
<span class="for-screen-reader"><%= bubble.title %></span>
<% end %>
<h2 contents>
<%= link_to bucket_bubble_path(bubble.bucket, bubble), class: "bubble__link" do %>
<span class="for-screen-reader"><%= bubble.title %></span>
<% end %>
</h2>
<%= render "bubbles/assignments", bubble: bubble %>
<%= render "bubbles/boosts", bubble: bubble %>
<%= render "bubbles/comments", bubble: bubble %>
<%= render "bubbles/date", bubble: bubble %>
<%= render "bubbles/image", bubble: bubble %>
</div>
<% end %>
<% end %>
+3 -2
View File
@@ -1,5 +1,6 @@
<% if bubble.messages.comments.any? %>
<div class="bubble__detail bubble__comments">
<span><%= bubble.messages.comments.count %></span>
<span aria-hidden="true"><%= bubble.messages.comments.count %></span>
<span class="for-screen-reader"><%= pluralize(bubble.messages.comments.count, "comment") %></span>
</div>
<% end %>
<% end %>
+1 -1
View File
@@ -1,9 +1,9 @@
<% if bubble.due_on.present? %>
<%= form_with model: bubble, url: bucket_bubble_path(bubble.bucket, bubble), data: { controller: "form" } do |form| %>
<label class="bubble__detail bubble__date">
<span class="for-screen-reader">Change the due date</span>
<span class="bubble__date-text"><%= html_escape bubble.due_on.strftime("%b <br> %d").html_safe %></span>
<%= form.date_field :due_on, class: "input input--hidden", data: { action: "change->form#submit click->form#showPicker" } %>
<span class="for-screen-reader">Change the due date</span>
</label>
<% end %>
<% end %>
+1 -1
View File
@@ -1,3 +1,3 @@
<div class="bubble__image">
<%= image_tag bubble.image.presence || "", data: { upload_preview_target: "image" } %>
<%= image_tag bubble.image.presence || "", data: { upload_preview_target: "image" }, aria: { hidden: true } %>
</div>
+14 -7
View File
@@ -1,6 +1,7 @@
<li class="<%= class_names("flex align-center gap-half margin-none", drafted: bubble.drafted?, popped: bubble.popped?) %>" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
data-controller="animation" data-animation-play-class="bubble--wobble" data-animation-play-on-load-value="true" data-action="mouseover->animation#play">
<%= link_to bucket_bubble_path(bubble.bucket, bubble), class: "flex-item-no-shrink" do %>
<%= link_to bucket_bubble_path(bubble.bucket, bubble), class: "flex-item-no-shrink",
aria: { hidden: true }, tabindex: -1 do %>
<div class="bubble__shape flex-item-no-shrink"></div>
<svg class="bubble__splat flex-item-no-shrink" viewBox="0 0 934 914" xmlns="http://www.w3.org/2000/svg"><circle cx="485.2" cy="467.2" r="286.5"/><path d="m197.7 219.1c-.2-7.1 5.8-11.7 12.7-13.2 34-7.1 53.5 29.2 81.4 40 39.5 6.4 25.9-54 30.8-77.7 17.2-65.3 78.4 67.2 108.2 55.7 26.8-17.3 25.9-58.8 38.7-86.1 9.6-41.5 30.4-2.6 48.6 10.1 14.2 11.2 31.9 17.9 45.1 29.9 13 11.2 20 29.1 35.8 36.8 20.2 9.1 44.8 2.3 65.2 11.8 18.3 7.2 33.8 22.6 52.5 30.4 23 8.6 42.4 1.8 37.8 35.1 2.7 36.8 38.3 71.9 40.1 108.6-.2 21.1-30.1 21.7-22.4 36.3 22.1 32.5-7.7 51.1 21 93.3 7 43.2-91.2-7.2-14.3 78.2 13.2 13.7 22.3 28.2 4.5 40.2-18.7 18.4-19.5 18.4-45 13.8-15.5-1.2 1.8 16.5-4.6 22.7-21.1 8-6.7 22-14.6 34.9-6 3.2-14.7-2.6-21.1-4-17.2-6.2-11.5 17.5-15.3 25.7-4 7.3-15.6 1.5-22.2-.2-26.7-10.8-55.1-3.1-82.4-.2-14.2 1.1-26 2.2-30.2 18.3-8 20.8-7.5 87.5-41.5 68.5-15.3-7.2-29.8-51.7-45.3-26.4-2.8 3.9-6.4 9.3-11.2 5.9-20.7-27.6-17-71-54.3-25.6-29.6 29.4-32.5 2.5-48.8-20.5-18.9-17.1-48.4-20.5-61.6-44.6-18.5-27.6-35.8-54.6-60.2-77.9-10.5-10.8-60.9-17.2-34.7-35.6 42.5-23.5-50.6-14-19.6-30.8 33.5-11.7 12.7-23.2-10.3-28.1-6.2-1.9-22-3.3-22.7-8.2 2.9-11.2 13.8-26.5 22.6-35.7 38.3-20.1-7-33.5-23.6-48.2-4-4.4 4.8-2.9 5.5-5.8-7.4-11.8-11.8-10-.4-21.8 3.2-7.8-4.9-17.6-3.8-25.5 8.1-21 57.9-6.5 64.2-36.2-3.5-26.1-16.4-50.3 3.5-75 5.9-8.8 22.4-16.8 19.3-28.7-5.7-14.7-26.4-23.7-27.6-40.2z"/><path d="m573 256.7s29.6-24.9 55-64.4 40.3-158 88.1-127.9c44.4 28-27.7 97.3-60.3 143.3-32.6 45.9-14.3 85.7-14.3 85.7l-68.5-36.6z"/><path d="m606.4 674.7s-11.3 37-11.2 84c0 47 51.9 154.6-4.6 155.3-52.5.6-29.6-96.8-27.1-153.1s-34.5-79.7-34.5-79.7z"/><path d="m325.5 622.7s-34 16.1-71.9 44c-37.8 27.9-104.9 118.2-126.8 89.7-20.3-26.5 66.8-72.5 113.1-104.6s51.2-64.9 51.2-64.9l34.4 35.9z"/><path d="m225.7 388.5s-30.8-35.1-75.8-72.7c-45.1-37.5-169.2-98.3-146.9-126.5 20.8-26.1 104.8 63 157.6 109.3 52.9 46.2 90.4 46.9 90.4 46.9l-25.4 43z"/><path d="m327.7 593.7s-47.6 1.4-103.9 17.7c-56.3 16.2-170.6 104.9-187.6 48.8-15.8-52.2 107.7-62.8 175.9-79.7s85.8-61.9 85.8-61.9l29.9 75.1z"/><path d="m443.5 344.2s0-46.7-8.9-104.6c-9-57.9-62-185.6-26.7-192 32.9-5.9 37 116.5 46.2 186.2 9.2 69.6 36.9 95 36.9 95l-47.5 15.5z"/><path d="m433.2 340.9s-6.8-37-19.5-82.8c-12.8-45.8-56.9-146.5-40.8-151.7 15-4.8 34.8 92 49.3 147s31.6 74.9 31.6 74.9l-20.6 12.5z"/><path d="m496.8 298.1s10.8-45.7 13.6-104.2c2.8-58.6-29.9-194.6 12.9-193.4 39.8 1.1 16.5 121.7 11.2 191.8-5.3 70 21.3 100.6 21.3 100.6l-59.1 5.2z"/><path d="m647.8 525s22.1 41.1 57.6 87.8c35.4 46.7 142.8 133.8 114.7 156.2-26.1 20.8-88-84.9-129.1-141.8-41.2-56.9-77.6-66-77.6-66z"/><path d="m658.4 523s23.5 29.3 56.5 63.5 119.7 101.9 108 114.1c-10.9 11.4-74.3-64.4-113.3-105.9s-63.4-50.9-63.4-50.9z"/><path d="m622.8 591s12.2 45.3 37.6 98.2 118.8 157 80.6 176.3c-35.6 17.9-72.4-99.3-101.1-163.4-28.6-64.1-66.6-78.4-66.6-78.4l49.5-32.6z"/><path d="m613.7 441.6s43.3 17.5 100.4 30.7c57.1 13.3 195.4 11.6 188.1 46.8-6.8 32.7-121.9-9.1-189.9-26.6s-101.9-1.2-101.9-1.2l3.3-49.8z"/><path d="m666.2 496.5s38.4 27.1 91.7 51.5 191.7 44.8 174.6 84.1c-15.9 36.5-119.1-30.1-182.2-61.1-63-31-101.3-17.7-101.3-17.7l17.2-56.7z"/><path d="m57.2 500.1c-7.1-1.3-14.4-2-21.5-.7-13.9 2.4-27.3 11.4-31.7 25.2-5 15.2 2.5 30.2 12.9 41.7 7.5 8.5 16.3 16.5 27.6 16.6 19.9-.7 36.3-19.9 37.6-39 1.6-18.3-8.2-36.2-27.2-40.2-3.5-.8-7.2-1.2-10.8-1.5"/><path d="m796.1 362.9c5.4 21.4 22.7 41 44.2 46.3 16.7 4.5 33.7-3 43.2-17.2 24.3-38.2-3-125.5-53.2-116.5-8 1.5-15.8 5.8-21.4 11.6-18.2 19.3-19.3 51.3-12.8 75.6z"/><path d="m255.1 782.9c5.4 21.4 22.7 41 44.2 46.3 16.7 4.5 33.7-3 43.2-17.2 24.3-38.2-3-125.5-53.2-116.5-8 1.5-15.8 5.8-21.4 11.6-18.2 19.3-19.3 51.3-12.8 75.6z"/><path d="m771 224.4c-3.4-.5-6.8-1-10.2-1.1-23.8-1.1-36.1 23.5-16.7 36.4 9.4 6.3 22.2 2.4 26.7-7.7 5.2-11.6-.7-20.3-10.6-26.9"/><path d="m402 812.4c-3.4-.5-6.8-1-10.2-1.1-23.8-1.1-36.1 23.5-16.7 36.4 9.4 6.3 22.2 2.4 26.7-7.7 5.2-11.6-.7-20.3-10.6-26.9"/></svg>
<% end %>
@@ -14,13 +15,18 @@
<% end %>
<% if bubble.messages.comments.many? %>
<span class="translucent txt-nowrap flex-item-no-shrink">(<%= bubble.messages.comments.count %>)</span>
<span class="translucent txt-nowrap flex-item-no-shrink">
<div aria-hidden="true">(<%= bubble.messages.comments.count %>)</div>
<div class="for-screen-reader"><%= pluralize(bubble.messages.comments.count, "comment") %></div>
</span>
<% end %>
<span class="flex-item-no-shrink flex">
<span class="flex-item-no-shrink flex" role="group" aria-label="Assignees">
<% bubble.assignees.each do |assignee| %>
<%= link_to bubbles_path(@filter.as_params.merge(assignee_ids: [ assignee.id ])), class: "btn avatar", style: "font-size: 0.4em;" do %>
<%= avatar_image_tag assignee, loading: :lazy %>
<%= link_to bubbles_path(@filter.as_params.merge(assignee_ids: [ assignee.id ])),
class: "btn avatar", style: "font-size: 0.4em;",
aria: { label: assignee.name } do %>
<%= avatar_image_tag assignee, loading: :lazy, aria: { hidden: true } %>
<% end %>
<% end %>
</span>
@@ -31,7 +37,8 @@
<% if bubble.due_on.present? %>
<strong class="txt-nowrap txt-small" style="color: <%= bubble.color %>">
<%= local_datetime_tag bubble.due_on, style: :shortdate %>
<div class="for-screen-reader">Due on <%= local_datetime_tag bubble.due_on, style: :shortdate %></div>
<div aria-hidden="true"><%= local_datetime_tag bubble.due_on, style: :shortdate %></div>
</strong>
<% end %>
</div>
@@ -41,4 +48,4 @@
<span class="flex-item-no-shrink"><%= render "bubbles/tags", bubble: bubble %></span>
</div>
</div>
</li>
</li>
+2 -2
View File
@@ -1,10 +1,10 @@
<li class="bubbles-list__divider flex align-center gap-half" draggable="true" data-divider-target="divider">
<div class="divider-drag-image" data-divider-target="dragImage">Drag up or down</div>
<hr class="separator--horizontal flex-item-grow">
<hr class="separator--horizontal flex-item-grow" aria-hidden="true">
<strong class="btn bubbles-list__count border-radius">
<%= image_tag "drag.svg", aria: { hidden: true }, size: 24, class: "colorize--white" %>
<span data-divider-target="count">10</span> Bubbled up
</strong>
<hr class="separator--horizontal flex-item-grow">
<hr class="separator--horizontal flex-item-grow" aria-hidden="true">
<span></span>
</li>
+4 -4
View File
@@ -56,11 +56,11 @@
<%= form_with model: @bubble, url: bucket_bubble_path(@bubble.bucket, @bubble), data: { controller: "form" } do |form| %>
<label class="btn full-width justify-start borderless min-width">
<%= image_tag "picture-add.svg", aria: { hidden: true }, size: 24 %>
<span class="overflow-ellipsis">Background</span>
<%= form.file_field :image, class: "input",
<span class="overflow-ellipsis" aria-hidden="true">Background</span>
<%= form.file_field :image, class: "input visually-hidden",
accept: "image/png, image/jpeg, image/jpg, image/webp",
data: { action: "upload-preview#previewImage form#submit", upload_preview_target: "input" },
hidden: true %>
aria: { label: "Upload background image" },
data: { action: "upload-preview#previewImage form#submit", upload_preview_target: "input" } %>
</label>
<% end %>
<% end %>
+3 -1
View File
@@ -1,5 +1,7 @@
<li class="bucket flex flex-column txt-align-center max-width">
<%= link_to bubbles_path(bucket_ids: [ bucket ]), class: "border border-radius margin-block-end-half bubbles__container flex justify-center align-center position-relative" do %>
<%= link_to bubbles_path(bucket_ids: [ bucket ]),
class: "border border-radius margin-block-end-half bubbles__container flex justify-center align-center position-relative",
aria: { hidden: true }, tabindex: -1 do %>
<div class="bubbles bucket__bubbles flex flex-wrap gap justify-center align-end" style="view-transition-name: bubbles_<%= bucket.id %>">
<% bucket.bubbles.active.published_or_drafted_by(Current.user).ordered_by_activity.limit(10).each do |bubble| %>
<div class="<%= class_names("bubble", drafted: bubble.drafted?) %>" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
+4 -4
View File
@@ -22,11 +22,11 @@
</nav>
<% end %>
<div class="buckets margin-block-double unpad align-start justify-center flex flex-wrap gap" data-controller="bubble-size">
<ul class="buckets margin-block-double unpad align-start justify-center flex flex-wrap gap" data-controller="bubble-size">
<%= render @buckets, cached: true %>
</div>
<div class="buckets margin-block-double unpad align-start justify-center flex flex-wrap gap" data-controller="bubble-size">
</ul>
<ul class="buckets margin-block-double unpad align-start justify-center flex flex-wrap gap" data-controller="bubble-size">
<%= render @filters %>
</div>
</ul>
<%= render "notifications/tray" %>
+9 -5
View File
@@ -2,12 +2,16 @@
<%= turbo_frame_tag dom_id(comment) do %>
<div class="comment__content flex-inline flex-column full-width border-radius">
<div class="comment__author flex align-center gap-half">
<strong><%= link_to comment.creator.name, user_path(comment.creator), class: "txt-ink btn btn--plain fill-transparent", data: { turbo_frame: "_top" } %></strong>
<%= link_to bucket_bubble_path(comment.bubble.bucket, comment.bubble, anchor: "comment_#{comment.id}"), class: "txt-undecorated" do %>
<%= local_datetime_tag comment.created_at, style: :shortdate, class: "comment__timestamp" %>
<% end %>
<h3 contents>
<strong><%= link_to comment.creator.name, user_path(comment.creator), class: "txt-ink btn btn--plain fill-transparent", data: { turbo_frame: "_top" } %></strong>
<%= link_to bucket_bubble_path(comment.bubble.bucket, comment.bubble, anchor: "comment_#{comment.id}"), class: "txt-undecorated" do %>
<%= local_datetime_tag comment.created_at, style: :shortdate, class: "comment__timestamp" %>
<% end %>
</h3>
<%= link_to edit_bucket_bubble_comment_path(comment.bubble.bucket, comment.bubble, comment), class: "btn txt-small btn--plain fill-transparent comment__edit", style: "font-size: 0.4em; opacity: 0.5;" do %>
<%= link_to edit_bucket_bubble_comment_path(comment.bubble.bucket, comment.bubble, comment), role: "button",
class: "btn txt-small btn--plain fill-transparent comment__edit",
style: "font-size: 0.4em; opacity: 0.5;" do %>
<%= image_tag "menu-dots-horizontal.svg", class: "icon colorize--black" %>
<span class="for-screen-reader">Edit</span>
<% end %>
+3 -3
View File
@@ -1,7 +1,7 @@
<%= comment_tag comment do %>
<figure class="comment__avatar flex-item-no-shrink">
<%= avatar_tag comment.creator, loading: :lazy %>
<figure class="comment__avatar flex-item-no-shrink" aria-hidden="true">
<%= avatar_tag comment.creator, hidden_for_screen_reader: true, loading: :lazy %>
</figure>
<%= render "comments/body", comment: comment %>
<% end %>
<% end %>
@@ -5,14 +5,16 @@
data-reaction-delete-reacter-id-value="<%= reaction.reacter.id %>"
data-controller="reaction-delete" data-reaction-delete-perform-class="reaction--deleting"
data-reaction-delete-reveal-class="expanded" data-reaction-delete-reacter-id-value="<%= reaction.reacter.id %>">
<figure class="reaction__avatar margin-none flex-item-no-shrink">
<%= avatar_tag reaction.reacter, aria: { label: "#{reaction.reacter.name} reacted #{reaction.content}" }, loading: :lazy %>
<figure class="reaction__avatar margin-none flex-item-no-shrink" aria-hidden="true">
<%= avatar_tag reaction.reacter, hidden_for_screen_reader: true, loading: :lazy %>
</figure>
<%= tag.span reaction.content, role: "button",
class: [ "txt-small", { "txt-medium": reaction.all_emoji? } ],
data: { action: "click->reaction-delete#reveal keydown.enter->reaction-delete#reveal:prevent", reaction_delete_target: "content" } %>
<%= tag.span reaction.content, class: [ "txt-small", { "txt-medium": reaction.all_emoji? } ],
data: {
action: "click->reaction-delete#reveal keydown.enter->reaction-delete#reveal:prevent",
reaction_delete_target: "content" } do %>
<span class="for-screen-reader"><%= "#{reaction.reacter.name} reacted #{reaction.content}" %></span>
<% end %>
<%= button_to bucket_bubble_comment_reaction_path(comment.bubble.bucket, comment.bubble, comment, reaction),
method: :delete,
class: "btn btn--negative flex-item-justify-end reaction__delete",
@@ -21,5 +23,4 @@
<span class="for-screen-reader">Delete this reaction</span>
<% end %>
</div>
<span id="delete_reaction_accessible_label" class="for-screen-reader">Press enter to delete this reaction</span>
<% end %>
<% end %>
@@ -7,7 +7,7 @@
<%= turbo_frame_tag comment, :new_reaction do %>
<div class="flex-inline" data-controller="soft-keyboard">
<%= link_to new_bucket_bubble_comment_reaction_path(comment.bubble.bucket, comment.bubble, comment),
<%= link_to new_bucket_bubble_comment_reaction_path(comment.bubble.bucket, comment.bubble, comment), role: "button",
class: "txt-small btn reaction__action", action: "soft-keyboard#open" do %>
<%= image_tag "reaction.svg", size: 20, aria: { hidden: "true" } %>
<span class="for-screen-reader">Add a reaction</span>
@@ -15,4 +15,4 @@
</div>
<% end %>
</div>
<% end %>
<% end %>
+6 -5
View File
@@ -1,14 +1,15 @@
<%= turbo_frame_tag dom_id(@comment, :new_reaction) do %>
<%= form_with url: bucket_bubble_comment_reactions_path(@comment.bubble.bucket, @comment.bubble, @comment),
class: "reaction reaction__form flex-inline postion--relative max-width align-center fill-white gap expanded",
html: { aria: { label: "New reaction" } },
data: { controller: "form", turbo_frame: dom_id(@comment, :reacting), action: "keydown.esc->form#cancel" } do |form| %>
<label class="reaction__form-label flex gap" style="--column-gap: 0.7ch;" role="button" tabindex="0" aria-label="Add a reaction">
<label class="reaction__form-label flex gap" style="--column-gap: 0.7ch;">
<figure class="reaction__avatar margin-none flex-item-no-shrink">
<%= avatar_tag Current.user %>
<span class="for-screen-reader"><%= Current.user.name %></span>
</figure>
<%= form.text_field :content, name: "reaction[content]", autofocus: true, autocomplete: "off", autocorrect: "off", maxlength: 16,
required: true, pattern: /\S+.*/, class: "input reaction__input txt-small" %>
required: true, pattern: /\S+.*/, class: "input reaction__input txt-small",
aria: { label: "Add a reaction" } %>
</label>
<%= form.button class: "btn btn--reversed", type: "submit" do %>
@@ -16,10 +17,10 @@
<span class="for-screen-reader">Submit</span>
<% end %>
<%= link_to bucket_bubble_comment_reactions_path(@comment.bubble.bucket, @comment.bubble, @comment),
<%= link_to bucket_bubble_comment_reactions_path(@comment.bubble.bucket, @comment.bubble, @comment), role: "button",
data: { turbo_frame: dom_id(@comment, :reactions), form_target: "cancel" }, class: "btn btn--negative" do %>
<%= image_tag "minus.svg", aria: { hidden: "true" } %>
<span class="for-screen-reader">Cancel</span>
<% end %>
<% end %>
<% end %>
<% end %>
+10 -7
View File
@@ -11,20 +11,22 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Assigned to…" aria-description="Assigned to…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half overflow-ellipsis">Assigned to…</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:assignee_ids, :assignment_status).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= link_to bubbles_path(filter.as_params.except(:assignee_ids, :assignment_status)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<div class="btn popup__item">
<%= form.check_box "assignment_status", {
<%= form.check_box :assignment_status, {
checked: filter.assignment_status.unassigned?,
data: { action: "change->form#submit" },
include_hidden: false,
@@ -33,19 +35,20 @@
<%= form.label :assignment_status, "No one", class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% Current.account.users.active.order(:name).each do |user| %>
<div class="btn popup__item">
<%= form.check_box "assignee_ids[]", {
<%= form.check_box :assignee_ids, {
multiple: true,
checked: filter.assignees.include?(user),
data: { action: "change->form#submit" },
include_hidden: false,
}, user.id %>
<%= form.label "assignee_ids[]", user.name, for: dom_id(user, :filter), class: "overflow-ellipsis" %>
<%= form.label :assignee_ids, user.name, for: form.field_id(:assignee_ids, user.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% end %>
</dialog>
</div>
</div>
+9 -6
View File
@@ -10,31 +10,34 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="In collection" aria-description="In collection"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">In collection</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:bucket_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= link_to bubbles_path(filter.as_params.except(:bucket_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<% filter.buckets.order(:name).each do |bucket| %>
<div class="btn popup__item">
<%= form.check_box "bucket_ids[]", {
<%= form.check_box :bucket_ids, {
multiple: true,
checked: filter.buckets.include?(bucket),
data: { action: "change->form#submit" },
include_hidden: false,
}, bucket.id %>
<%= form.label "bucket_ids[]", bucket.name, for: dom_id(bucket, :filter), class: "overflow-ellipsis" %>
<%= form.label :bucket_ids, bucket.name, for: form.field_id(:bucket_ids, bucket.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% end %>
</dialog>
</div>
<% end %>
<% end %>
+11 -8
View File
@@ -12,42 +12,45 @@
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
aria-label="Added by…" aria-description="Added by…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">Added by…</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:creator_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= link_to bubbles_path(filter.as_params.except(:creator_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<div class="btn popup__item">
<%= form.check_box "creator_ids[]", {
<%= form.check_box :creator_ids, {
multiple: true,
checked: filter.creators.include?(Current.user),
data: { action: "change->form#submit" },
include_hidden: false,
}, Current.user.id %>
<%= form.label "creator_ids[]", "Me", for: dom_id(Current.user, :creator_filter), class: "overflow-ellipsis" %>
<%= form.label :creator_ids, "Me", for: form.field_id(:creator_ids, Current.user.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% Current.account.users.active.without(Current.user).order(:name).each do |user| %>
<div class="btn popup__item">
<%= form.check_box "creator_ids[]", {
<%= form.check_box :creator_ids, {
multiple: true,
checked: filter.creators.include?(user),
data: { action: "change->form#submit" },
include_hidden: false,
}, user.id %>
<%= form.label "creator_ids[]", user.name, for: dom_id(user, :creator_filter), class: "overflow-ellipsis" %>
<%= form.label :creator_ids, user.name, for: form.field_id(:creator_ids, user.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% end %>
</dialog>
</div>
<% end %>
<% end %>
+93 -90
View File
@@ -1,4 +1,4 @@
<dialog class="filter__popup dialog panel fill-white shadow" data-dialog-target="dialog">
<dialog class="filter__popup dialog panel fill-white shadow" aria-label="Filter" aria-description="Filter" data-dialog-target="dialog">
<div class="flex flex-column gap-half">
<div class="flex align-center gap-half justify-space-between">
<span class="btn btn--placeholder txt-small" aria-hidden="true"></span>
@@ -25,7 +25,7 @@
<%= search_field_tag "terms[]", nil, class: "input full-width", placeholder: "By keyword…", id: nil %>
<button class="btn">
<span class="for-screen-reader">Apply</span>
<span class="for-screen-reader">Apply</span>
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
</button>
<% end %>
@@ -37,127 +37,130 @@
<% end %>
<div class="filter__columns flex gap">
<menu class="filter__menu">
<li class="filter__label"><strong>Sort by</strong></li>
<li>
<%= label_tag "indexed_by_#{filter.default_indexed_by}", class: "btn filter__button" do %>
<%= radio_button_tag "indexed_by", filter.default_indexed_by, filter.default_indexed_by?, hidden: true %>
<div class="filter__menu" role="group" aria-label="Sort by">
<div class="filter__label"><strong>Sort by</strong></div>
<div>
<label class="btn filter__button">
<%= radio_button_tag "indexed_by", filter.default_indexed_by, filter.default_indexed_by?, class: "visually-hidden" %>
<span>Most active</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% Filter::INDEXES.each do |index| %>
<li>
<%= label_tag "indexed_by_#{index}", class: "btn filter__button" do %>
<%= radio_button_tag "indexed_by", index, filter.indexed_by == index, hidden: true %>
<div>
<label class="btn filter__button">
<%= radio_button_tag "indexed_by", index, filter.indexed_by == index, class: "visually-hidden" %>
<span><%= index.humanize %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% end %>
</menu>
</div>
<menu class="filter__menu">
<li class="filter__label"><strong>In Collection</strong></li>
<div class="filter__menu" role="group" aria-label="In Collection">
<div class="filter__label"><strong>In Collection</strong></div>
<li>
<div>
<%= button_tag type: :button, class: "btn filter__button", data: { action: "filter-form#clearCategory", filter_form_name_param: "bucket_ids[]" } do %>
<span>All Collections</span>
<% end %>
</li>
</div>
<% Current.user.buckets.order(:name).each do |bucket| %>
<li>
<%= label_tag "bucket_ids_#{bucket.id}", class: "btn filter__button" do %>
<%= check_box_tag "bucket_ids[]", bucket.id, filter.buckets.include?(bucket), id: "bucket_ids_#{bucket.id}", hidden: true %>
<div>
<label class="btn filter__button">
<%= check_box_tag "bucket_ids[]", bucket.id, filter.buckets.include?(bucket), class: "visually-hidden" %>
<span><%= bucket.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% end %>
</menu>
</div>
<menu class="filter__menu">
<li class="filter__label"><strong>Tagged</strong></li>
<div class="filter__menu" role="group" aria-label="Tagged">
<div class="filter__label"><strong>Tagged</strong></div>
<% Current.account.tags.order(:title).each do |tag| %>
<li>
<%= label_tag "tag_ids_#{tag.id}", class: "btn filter__button" do %>
<%= check_box_tag "tag_ids[]", tag.id, filter.tags.include?(tag), id: "tag_ids_#{tag.id}", hidden: true %>
<div>
<label class="btn filter__button">
<%= check_box_tag "tag_ids[]", tag.id, filter.tags.include?(tag), class: "visually-hidden" %>
<span><%= tag.hashtag %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% end %>
</menu>
</div>
<menu class="filter__menu">
<li class="filter__label"><strong>Assigned to…</strong></li>
<li>
<%= label_tag "assignment_status_unassigned", class: "btn filter__button" do %>
<%= check_box_tag "assignment_status", "unassigned", filter.assignment_status.unassigned?, id: "assignment_status_unassigned",
hidden: true, data: { action: "filter-form#clearCategory", filter_form_name_param: "assignee_ids[],assigner_ids[]" } %>
<span>No one</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
<li>
<%= label_tag "assignee_ids_me", class: "btn filter__button" do %>
<%= check_box_tag "assignee_ids[]", Current.user.id, filter.assignees.include?(Current.user), id: "assignee_ids_me",
hidden: true, data: { action: "filter-form#clearCategory", filter_form_name_param: "assignment_status" } %>
<span>Me</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
<div class="filter__menu" role="group" aria-label="Assigned to…">
<div class="filter__label"><strong>Assigned to…</strong></div>
<div>
<label class="btn filter__button">
<%= check_box_tag "assignment_status", "unassigned", filter.assignment_status.unassigned?,
class: "visually-hidden",
data: {
action: "filter-form#clearCategory",
filter_form_name_param: "assignee_ids[],assigner_ids[]" } %>
<span>No one</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
</label>
</div>
<div>
<label class="btn filter__button">
<%= check_box_tag "assignee_ids[]", Current.user.id, filter.assignees.include?(Current.user),
class: "visually-hidden",
data: { action: "filter-form#clearCategory", filter_form_name_param: "assignment_status" } %>
<span>Me</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
</label>
</div>
<% Current.account.users.active.without(Current.user).order(:name).each do |user| %>
<li>
<%= label_tag "assignee_ids_#{user.id}", user.name, class: "btn filter__button" do %>
<%= check_box_tag "assignee_ids[]", user.id, filter.assignees.include?(user), id: "assignee_ids_#{user.id}",
hidden: true, data: { action: "filter-form#clearCategory", filter_form_name_param: "assignment_status" } %>
<span><%= user.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
<% end %>
</menu>
<menu class="filter__menu">
<li class="filter__label"><strong>Added by…</strong></li>
<li>
<%= label_tag "creator_ids_me", class: "btn filter__button" do %>
<%= check_box_tag "creator_ids[]", Current.user.id, filter.creators.include?(Current.user), id: "creator_ids_me",
hidden: true %>
<span>Me</span>
<div>
<label class="btn filter__button">
<%= check_box_tag "assignee_ids[]", user.id, filter.assignees.include?(user), class: "visually-hidden",
data: { action: "filter-form#clearCategory", filter_form_name_param: "assignment_status" } %>
<span><%= user.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
<% Current.account.users.active.without(Current.user).order(:name).each do |user| %>
<li>
<%= label_tag "creator_ids_#{user.id}", class: "btn filter__button" do %>
<%= check_box_tag "creator_ids[]", user.id, filter.creators.include?(user), id: "creator_ids_#{user.id}",
hidden: true %>
<span><%= user.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% end %>
</menu>
</div>
<menu class="filter__menu">
<li class="filter__label"><strong>In Stage</strong></li>
<div class="filter__menu" role="group" aria-label="Added by…">
<div class="filter__label"><strong>Added by…</strong></div>
<div>
<label class="btn filter__button">
<%= check_box_tag "creator_ids[]", Current.user.id, filter.creators.include?(Current.user),
class: "visually-hidden" %>
<span>Me</span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
</label>
</div>
<% Current.account.users.active.without(Current.user).order(:name).each do |user| %>
<div>
<label class="btn filter__button">
<%= check_box_tag "creator_ids[]", user.id, filter.creators.include?(user), class: "visually-hidden" %>
<span><%= user.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
</label>
</div>
<% end %>
</div>
<div class="filter__menu" role="group" aria-label="In Stage">
<div class="filter__label"><strong>In Stage</strong></div>
<% Current.account.workflows.order(:name).each do |workflow| %>
<li class="filter__label"><strong><%= workflow.name %></strong></li>
<div class="filter__label"><strong><%= workflow.name %></strong></div>
<% workflow.stages.order(:name).each do |stage| %>
<li>
<%= label_tag "stage_ids_#{stage.id}", class: "btn filter__button" do %>
<%= check_box_tag "stage_ids[]", stage.id, filter.stages.include?(stage), id: "stage_ids_#{stage.id}", hidden: true %>
<div>
<label class="btn filter__button">
<%= check_box_tag "stage_ids[]", stage.id, filter.stages.include?(stage), class: "visually-hidden" %>
<span><%= stage.name %></span>
<%= image_tag "close.svg", aria: { hidden: true }, size: 24 %>
<% end %>
</li>
</label>
</div>
<% end %>
<% end %>
</menu>
</div>
</div>
<% end %>
+3 -1
View File
@@ -1,6 +1,8 @@
<% cache_if filter.cacheable?, filter do %>
<li class="bucket flex flex-column txt-align-center max-width position-relative">
<%= link_to bubbles_path(**filter.as_params), class: "border border-radius margin-block-end-half bubbles__container flex justify-center align-center position-relative" do %>
<%= link_to bubbles_path(**filter.as_params),
class: "border border-radius margin-block-end-half bubbles__container flex justify-center align-center position-relative",
aria: { hidden: true }, tabindex: -1 do %>
<div class="bubbles bucket__bubbles flex flex-wrap gap justify-center align-end" style="view-transition-name: bubbles_<%= filter.id %>">
<% filter.bubbles.ordered_by_activity.limit(10).each do |bubble| %>
<div class="<%= class_names("bubble", drafted: bubble.drafted?) %>" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
+4 -2
View File
@@ -5,7 +5,9 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Sort by…" aria-description="Sort by…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">Sort by…</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
@@ -35,4 +37,4 @@
<% end %>
<% end %>
</dialog>
</div>
</div>
+14 -11
View File
@@ -10,35 +10,38 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="In stage…" aria-description="In stage…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half">In stage…</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:stage_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= link_to bubbles_path(filter.as_params.except(:stage_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<% Current.account.workflows.order(:name).each do |workflow| %>
<strong class="popup__title margin-block-start-half pad-inline-half flex overflow-ellipsis"><%= workflow.name %></strong>
<% workflow.stages.order(:name).each do |stage| %>
<strong class="popup__title margin-block-start-half pad-inline-half flex overflow-ellipsis"><%= workflow.name %></strong>
<% workflow.stages.order(:name).each do |stage| %>
<div class="btn popup__item">
<%= form.check_box "stage_ids[]", {
<%= form.check_box :stage_ids, {
multiple: true,
checked: filter.stages.include?(stage),
data: { action: "change->form#submit" },
include_hidden: false,
}, stage.id %>
<%= form.label "stage_ids[]", stage.name, for: dom_id(stage, :filter), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
<%= form.label :stage_ids, stage.name, for: form.field_id(:stage_ids, stage.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
</dialog>
</div>
<% end %>
<% end %>
+9 -6
View File
@@ -9,30 +9,33 @@
</span>
</button>
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<dialog class="events__popup popup panel flex-column align-start gap-half fill-white shadow"
aria-label="Tagged…" aria-description="Tagged…"
data-dialog-target="dialog" data-action="turbo:before-cache@document->dialog#close">
<strong class="popup__title margin-block-start-half pad-inline-half overflow-ellipsis">Tagged…</strong>
<%= form_with url: bubbles_path, method: :get, class: "flex flex-column full-width popup__list",
data: { controller: "form" } do |form| %>
<% filter.as_params.except(:tag_ids).each do |key, value| %>
<%= filter_hidden_field_tag key, value %>
<% end %>
<%= link_to bubbles_path(filter.as_params.except(:tag_ids)), class: "btn popup__item" do %>
<span class="overflow-ellipsis">Clear all</span>
<% end %>
<% Current.account.tags.order(:title).each do |tag| %>
<div class="btn popup__item">
<%= form.check_box "tag_ids[]", {
<%= form.check_box :tag_ids, {
multiple: true,
checked: filter.tags.include?(tag),
data: { action: "change->form#submit" },
include_hidden: false,
}, tag.id %>
<%= form.label "tag_ids[]", tag.hashtag, for: dom_id(tag, :filter), class: "overflow-ellipsis" %>
<%= form.label :tag_ids, tag.hashtag, for: form.field_id(:tag_ids, tag.id), class: "overflow-ellipsis" %>
<%= image_tag "check.svg", aria: { hidden: true }, size: 18, class: "checked flex-item-justify-end" %>
</div>
<% end %>
<% end %>
</dialog>
</div>
</div>