diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index dd9db411c94f73b19c71801f5a9114e4afaef658..3fa38dcfe0b1db56a22459359bd6de8ced45cbc8 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -59,9 +59,75 @@
   border-right: 1px dashed gray;
 }
 
+/* Role-based visibility classes */
+[class*="-only"] {
+  display: none;
+}
+
+/* Permission-based visibility classes */
+.can-manage-assignments-only {
+  display: none;
+}
+body[data-can-manage-assignments] .can-manage-assignments-only {
+  display: block;
+}
+
+/* Role-based visibility classes */
+.has-role-shift_coordinator-only {
+  display: none;
+}
+body[data-has-role-shift_coordinator] .has-role-shift_coordinator-only {
+  display: block;
+}
+
+.has-role-admin-only {
+  display: none;
+}
+body[data-has-role-admin] .has-role-admin-only {
+  display: block;
+}
+
+.has-role-events_admin-only {
+  display: none;
+}
+body[data-has-role-events_admin] .has-role-events_admin-only {
+  display: block;
+}
+
+/* Exception classes */
+.except-has-role-admin {
+  display: block;
+}
+body[data-has-role-admin] .except-has-role-admin {
+  display: none;
+}
+
+.except-has-role-shift_coordinator {
+  display: block;
+}
+body[data-has-role-shift_coordinator] .except-has-role-shift_coordinator {
+  display: none;
+}
+
+.except-has-role-events_admin {
+  display: block;
+}
+body[data-has-role-events_admin] .except-has-role-events_admin {
+  display: none;
+}
+
+.except-can-manage-assignments {
+  display: block;
+}
+body[data-can-manage-assignments] .except-can-manage-assignments {
+  display: none;
+}
+
+/* Legacy class for backward compatibility */
 .hide-unless-shiftcoordinator {
   display: none;
 }
-body[data-is-shiftcoordinator] .hide-unless-shiftcoordinator {
+body[data-has-role-shift_coordinator] .hide-unless-shiftcoordinator,
+body[data-can-manage-assignments] .hide-unless-shiftcoordinator {
   display: block;
 }
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 7f9c10f9ca4d21ba31fa03b5c3b774a7f7c00774..8a5f054e121854b1cfeefdd9b58a8c0724715041 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,9 +1,27 @@
 module ApplicationHelper
   def body_data_attributes
     attributes = {}
-    attributes[:loggedin_uid] = current_user.id if user_signed_in?
-    attributes[:is_shiftcoordinator] = 1 if current_user&.shiftcoordinator?
-    attributes[:languages_from] = current_user.languages_from unless current_user&.languages_from.blank?
+    if user_signed_in?
+      attributes[:loggedin_uid] = current_user.id
+
+      # Legacy attribute for backward compatibility
+      attributes[:is_shiftcoordinator] = 1 if current_user.shiftcoordinator?
+
+      # Add role-based data attributes
+      current_user.roles.each do |role|
+        attributes[:"has_role_#{role.name}"] = 1
+      end
+
+      # Add permission-based data attributes
+      current_user.roles.each do |role|
+        role.permissions.each do |permission|
+          attributes[:"can_#{permission.name}"] = 1
+        end
+      end
+
+      attributes[:languages_from] = current_user.languages_from unless current_user.languages_from.blank?
+    end
+
     attributes[:darkmode] = current_user&.darkmode || "auto"
     { data: attributes }
   end
diff --git a/app/views/sessions/_assignment_form.html.erb b/app/views/sessions/_assignment_form.html.erb
index ad2041d7ed7671224f20a60e73ff8553ebb87ba2..a775360a62bbd5e7baef0c4e391fc31f4d9def09 100644
--- a/app/views/sessions/_assignment_form.html.erb
+++ b/app/views/sessions/_assignment_form.html.erb
@@ -1,6 +1,6 @@
 <% unassigned_users = @users - session.assignments.collect(&:user) %>
 <% if unassigned_users.length > 0 %>
-  <div class="text-sm hide-unless-shiftcoordinator">
+  <div class="text-sm can-manage-assignments-only">
     <%= form_with url: conference_session_assignments_path(session.conference, session), method: :post, data: { turbo_frame: dom_id(session) } do |f| %>
       <%= f.select :user_id, options_from_collection_for_select(unassigned_users, :id, :name), { disabled: '', prompt: '-' }, { class: "text-sm" } %>
       <%= f.submit "Assign", class: 'primary text-sm' %>