diff --git a/README.md b/README.md
index 57b0ce5fccd0f14153e4d86987a82e44e14d72f8..8ba8a8cdf6ac2708c9a5b83571c0b70b594977f6 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,21 @@ tbd.
 
 See [rescheduled-deploy](https://git.cccv.de/c3lingo/rescheduled-deploy) for a full docker-compose stack and more explanations.
 
+# Colors (darkmode)
+
+- **text**: text-slate-300
+- **text (overlay)**: text-slate-200
+- **text light**: text-slate-400
+
+- **logo**: text-white
+- **highlight**: text-red-500
+
+- **dark background**: bg-zinc-700
+- **light background**: bg-gray-900
+- **overlay**: bg-gray-600
+- **input**: bg-zinc-900
+- **shadow**: gray-400
+
 ## Tips and Tricks
 
 ### Helpful `rails` tasks
diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index beacefe26053f5ccc759fc6d76348e2717dfa984..8e1ab0e1be5f38ed361c8ce0e602c6284956c9b4 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -21,9 +21,24 @@ input[type=submit] {
     @apply bg-teal-800 text-teal-50 border-teal-600;
   }
 }
-select {
+
+select, [type=text], [type=password] {
   @apply pl-2 pr-6 py-1;
 }
+.dark {
+  input[type=submit] {
+    @apply bg-gray-600 text-slate-200;
+  }
+  select, [type=text], [type=password] {
+    @apply bg-zinc-900;
+  }
+  .session-holder {
+    select, [type=text], [type=password] {
+      @apply bg-white/60;
+    }
+  }
+}
+
 .session-holder {
   @apply w-full;
   height: var(--height);
@@ -85,4 +100,4 @@ select {
       @apply ml-6;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c964fdea1e943936cb295e287256ea03b2a8ea89..4da35cb171d4ead463de7a883f1ba4fce9a0cbb5 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -6,7 +6,7 @@ class ApplicationController < ActionController::Base
   def configure_permitted_parameters
     devise_parameter_sanitizer.permit(:sign_up, keys: [:invitation_token])
     devise_parameter_sanitizer.permit(:account_update) do |u|
-      u.permit(:name, :email, :password, :password_confirmation, :avatar_color, :languages_from, :languages_to, :telegram_username, :current_password)
+      u.permit(:name, :email, :password, :password_confirmation, :avatar_color, :darkmode, :languages_from, :languages_to, :telegram_username, :current_password)
     end
   end
 
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d1a9e8a08ab63ba1097e3821ff486f6da2e11829..268d351fe30570bba8a9fc15add25e88d6717b86 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -4,6 +4,7 @@ module ApplicationHelper
     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?
+    attributes[:darkmode] = current_user.darkmode
     { data: attributes }
   end
 end
diff --git a/app/javascript/application.js b/app/javascript/application.js
index 0c66ece2255f65daf4262c959d1e4e3f8b650a61..9a7c38e3c09de067a5b333670776a0adfcc2d9e5 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -4,6 +4,7 @@ import "@hotwired/turbo-rails"
 
 document.addEventListener("turbo:load", function() {
   console.log('turbo:load');
+  applyDarkmode();
   const flashMessages = document.querySelectorAll(".flash");
 
   flashMessages.forEach(flashMessage => {
diff --git a/app/models/user.rb b/app/models/user.rb
index 1652be201b8e4276a2f034f57975d1d063d06e39..e54cc9c986cc05eae2cdd5875051dc26f52c3993 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -3,6 +3,9 @@ class User < ApplicationRecord
   has_many :assignments
   has_many :candidates
 
+  enum darkmode: { auto: 0, light: 1, dark: 2 }
+  validates :darkmode, inclusion: { in: %w(auto light dark) }
+
   validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
   validates :name, uniqueness: { case_sensitive: false, message: "already in use" }, allow_nil: false
   validates :email, uniqueness: { case_sensitive: false, message: "already in use" }, allow_nil: true, allow_blank: true
diff --git a/app/views/assignments/by_user.html.erb b/app/views/assignments/by_user.html.erb
index bb5542c9325d0cad6ece919c695415d462a098cd..9f246703ec5cb4765c46e78229cd0ea8d4b8f23f 100644
--- a/app/views/assignments/by_user.html.erb
+++ b/app/views/assignments/by_user.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h1 class="text-xl my-4">
+  <h1 class="text-xl my-4 dark:text-red-500">
     Assignments for
     <%= link_to @user.name, user_assignments_path(@user) %>
     <span class="text-base ml-2 mb-2 inline p-2 border bg-slate-50 hover:bg-slate-100 border-slate-200 hover:border-slate-200 shadow font-normal rounded-md"><%= link_to user_assignments_path(@user, format: 'ics') do %><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 24" fill="currentColor" aria-hidden="true" class="size-6 inline-block stroke-slate-400 fill-slate-400"><path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd"></path></svg> iCal<% end %></span>
diff --git a/app/views/assignments/index.html.erb b/app/views/assignments/index.html.erb
index 470a8154d28361bdfc4a55cbe6cce612a114b46e..8b61ac629bc1220ff21f976db6eebaed9527e117 100644
--- a/app/views/assignments/index.html.erb
+++ b/app/views/assignments/index.html.erb
@@ -1,6 +1,6 @@
 <% now = Time.now %>
 <div class="scroll-smooth">
-  <h1 class="text-xl my-4">Assignments for all users</h1>
+  <h1 class="text-xl my-4 dark:text-red-500">Assignments for all users</h1>
   <p>
     Jump to:
     <ul class="flex flex-row flex-wrap">
diff --git a/app/views/assignments/show.html.erb b/app/views/assignments/show.html.erb
index 6c530750c9b9ffc4b68ac98681587d7fb112c510..be9f69e60f620716fb665e933dd0b66ace8306df 100644
--- a/app/views/assignments/show.html.erb
+++ b/app/views/assignments/show.html.erb
@@ -1,4 +1,4 @@
 <div>
-  <h1 class="font-bold text-4xl">Assignments#show</h1>
+  <h1 class="font-bold text-4xl dark:text-red-500">Assignments#show</h1>
   <p>Find me in app/views/assignments/show.html.erb</p>
 </div>
diff --git a/app/views/conferences/index.html.erb b/app/views/conferences/index.html.erb
index 7de75c246d0cbff21a9cf3dfdbb55f6de3dc561d..4fcb2ed25ad66e45617c899d1fca999fc7a653c6 100644
--- a/app/views/conferences/index.html.erb
+++ b/app/views/conferences/index.html.erb
@@ -1,8 +1,8 @@
 <div>
-<h1 class="text-xl my-4">Conferences</h1>
+<h1 class="text-xl my-4 dark:text-red-500">Conferences</h1>
 <ul>
 <% @conferences.each do |conference| %>
-<li><%= link_to conference.name, conference_path(conference.slug), class: "inline-block px-4 py-2 text-slate-500 hover:text-slate-900 hover:bg-slate-100 rounded-md" %></li>
+<li><%= link_to conference.name, conference_path(conference.slug), class: "inline-block px-4 py-2 text-slate-500 hover:text-slate-900 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-gray-600 rounded-md" %></li>
 <% end %>
 </ul>
 </div>
diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb
index 8c17f0e87eaa024531583e203a539f227c1f4963..0528867b2799f0209425e48ffdc4fbe9bd61a90d 100644
--- a/app/views/conferences/show.html.erb
+++ b/app/views/conferences/show.html.erb
@@ -17,7 +17,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
   <div>
     <a href="#now" onclick="document.querySelector('#now')?.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); return false" class="underline text-blue-500">Jump to current time</a>
   </div>
-  <h1 class="text-2xl font-bold my-2"><%= @conference.name %></h1>
+  <h1 class="text-2xl font-bold my-2 dark:text-red-500"><%= @conference.name %></h1>
   <p class="text-xs mb-6">
     <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block size-4 stroke-slate-500">
       <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
@@ -58,7 +58,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
     timeline_ends_at = day_ends_at.advance(minutes: (day_ends_at.min / timeline_granularity.to_f).ceil * timeline_granularity)
   %>
     <div class="conference-day my-8" id="<%= date.strftime('day-%Y-%m-%d') %>">
-      <h3 class="text-xl my-4 border-b sticky top-0 md:top-[3.7rem] bg-white bg-opacity-70 z-30"><%= date.strftime('%B %d, %Y') %></h3>
+      <h3 class="text-xl my-4 border-b sticky top-0 md:top-[3.7rem] bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 z-30"><%= date.strftime('%B %d, %Y') %></h3>
       <%
 =begin%>
       <ul class="list-disc">
@@ -71,7 +71,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
 =end %>
       <div class="day-wrapper flex relative">
 
-      <div class="times sticky left-0 bg-white bg-opacity-70 z-20">
+      <div class="times sticky left-0 bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 z-20">
         <h4>Time</h4>
         <% # if current_time.strftime('%Y%m%d') == date.strftime('%Y%m%d') && current_time >= timeline_starts_at && current_time <= timeline_ends_at
         %>
@@ -131,7 +131,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
         sessions = @sessions_by_date_and_stage[date][stage]
       %>
           <div class="stage">
-            <h4 class="sticky md:top-[5.5rem] top-7 bg-white bg-opacity-70 w-full z-30"><%= stage.name %></h4>
+            <h4 class="sticky md:top-[5.5rem] top-7 bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 w-full z-30"><%= stage.name %></h4>
             <div class="stage-sessions">
               <% sessions.each do |session| %>
                 <div class="session-holder hover:z-30 h-full" style="
diff --git a/app/views/conferences/stats.html.erb b/app/views/conferences/stats.html.erb
index 8465f00dc867802462673280fa0f0324bdfd0266..5ae8cfba1e8c4e07965dc7d749c400d430889ac0 100644
--- a/app/views/conferences/stats.html.erb
+++ b/app/views/conferences/stats.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h1>Statistics for <%= @conference.name %></h1>
+  <h1 class="dark:text-red-500">Statistics for <%= @conference.name %></h1>
   <div class="conference-stats">
     <ul>
       <li><%= @assignees.count %> interpreters
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index b517dde8e4bb763aedfffe5731ba39fa3ea5e9b0..5fa03b782af2ea8e40716efc98d77c7ceecfeecb 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -1,6 +1,6 @@
 <div>
 
-<h1 class="text-xl my-4">Profile</h1>
+<h1 class="text-xl my-4 dark:text-red-500">Profile</h1>
 
 <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
   <%= render "devise/shared/error_messages", resource: resource %>
@@ -33,6 +33,11 @@
     <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
   </div>
 
+  <div class="field">
+    <%= f.label :darkmode %>
+    <%= f.select :darkmode, User.darkmodes.keys.map { |d| [d.humanize, d] } %>
+  </div>
+
   <div class="field">
     <%= f.label :avatar_color %>
     <%= f.color_field :avatar_color %>
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
index 5d98cd4b945fe559173183829dbe3674b12ee278..b532322b03529d3c772b9ba205e2ad29f27337e4 100644
--- a/app/views/devise/registrations/new.html.erb
+++ b/app/views/devise/registrations/new.html.erb
@@ -1,5 +1,5 @@
 <div>
-<h1 class="text-xl my-4">Sign Up</h1>
+<h1 class="text-xl my-4 dark:text-red-500">Sign Up</h1>
 
 <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
   <%= render "devise/shared/error_messages", resource: resource %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
index a216b2d1e6271e331fee648c223f4e8191528051..70ca0530173d0ca5699ef60e32786f84287311d5 100644
--- a/app/views/devise/sessions/new.html.erb
+++ b/app/views/devise/sessions/new.html.erb
@@ -1,5 +1,5 @@
 <div>
-<h1 class="text-xl my-4">Log in</h1>
+<h1 class="text-xl my-4 dark:text-red-500">Log in</h1>
 
 <%= form_for(resource, as: resource_name, url: user_session_path) do |f| %>
   <div class="field">
diff --git a/app/views/filedrop_files/download.html.erb b/app/views/filedrop_files/download.html.erb
index 60788e4b97e71b53eed212342259dba7d003dc0c..a1ee394e5b8a7f4c732c01fbc236199e83dabd86 100644
--- a/app/views/filedrop_files/download.html.erb
+++ b/app/views/filedrop_files/download.html.erb
@@ -1,4 +1,4 @@
 <div>
-  <h1 class="font-bold text-4xl">FiledropFiles#download</h1>
+  <h1 class="font-bold text-4xl dark:text-red-500">FiledropFiles#download</h1>
   <p>Find me in app/views/filedrop_files/download.html.erb</p>
 </div>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 7a482d3a8a2bd8f1a6854a15a9e3694f2b96e4d0..e2c68a9208b559b3bba7c6359754b2f862ce4e47 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -9,52 +9,65 @@
 
     <%= stylesheet_link_tag "application" %>
     <%= javascript_importmap_tags %>
+
+    <script type="text/javascript">
+      function applyDarkmode() {
+        const userTheme = document.body.dataset.darkmode;
+        document.documentElement.classList.toggle(
+          'dark',
+          userTheme === 'dark' || ((userTheme === 'auto') && window.matchMedia('(prefers-color-scheme: dark)').matches)
+        );
+      }
+    </script>
   </head>
 
-  <body <%= tag.attributes(body_data_attributes) %>>
-  <nav class="bg-slate-100 text-gray-700 shadow w-full relative md:fixed z-50" data-controller="navigation">
-    <div class="relative bg-slate-100 z-50 container mx-auto px-4 py-4 flex justify-between items-center">
+  <body <%= tag.attributes(body_data_attributes) %> class="dark:bg-gray-900 dark:text-slate-300">
+  <script type="text/javascript">
+    applyDarkmode(); // Required to prevent flashing on load
+  </script>
+  <nav class="bg-slate-100 dark:bg-zinc-700 text-gray-700 dark:text-slate-300 shadow w-full relative md:fixed z-50" data-controller="navigation">
+    <div class="relative bg-slate-100 dark:bg-zinc-700 z-50 container mx-auto px-4 py-4 flex justify-between items-center">
       <div class="flex items-center space-x-4">
-        <div class="text-xl font-bold text-black"><%= link_to 're:scheduled', '/', class: "!no-underline" %></div>
+        <div class="text-xl font-bold text-black dark:text-white"><%= link_to 're:scheduled', '/', class: "!no-underline" %></div>
         <div class="hidden md:flex space-x-4">
-          <%= link_to 'Conferences', conferences_path, class: 'hover:text-gray-900' %>
-          <%= link_to 'Assignments', assignments_path, class: 'hover:text-gray-900' %>
+          <%= link_to 'Conferences', conferences_path, class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
+          <%= link_to 'Assignments', assignments_path, class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
         </div>
       </div>
       <div class="hidden md:flex items-center space-x-4 ml-0 lg:ml-8">
         <% if user_signed_in? %>
         <span class="-mr-2 hidden lg:inline">logged in as</span>
         <%= render partial: 'application/user_avatar', locals: { user: current_user } %>
-        <%= link_to '<span class="hidden lg:inline">My </span>Profile'.html_safe, edit_user_registration_path, class: 'hover:text-gray-900', aria_label: "My Profile" %>
-        <%= link_to 'My Assignments', user_assignments_path(current_user), class: 'hover:text-gray-900' %>
-        <%= link_to 'Logout', destroy_user_session_path, data: { turbo_method: :delete }, class: 'hover:text-gray-900' %>
+        <%= link_to '<span class="hidden lg:inline">My </span>Profile'.html_safe, edit_user_registration_path, class: 'hover:text-gray-900 dark:hover:text-slate-200', aria_label: "My Profile" %>
+        <%= link_to 'My Assignments', user_assignments_path(current_user), class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
+        <%= link_to 'Logout', destroy_user_session_path, data: { turbo_method: :delete }, class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
         <% else %>
         <span class="px-2">not logged in</span>
-        <%= link_to 'Login', new_user_session_path, class: 'hover:text-gray-900' %>
-        <%= link_to 'Sign Up', new_user_registration_path, class: 'hover:text-gray-900' %>
+        <%= link_to 'Login', new_user_session_path, class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
+        <%= link_to 'Sign Up', new_user_registration_path, class: 'hover:text-gray-900 dark:hover:text-slate-200' %>
         <% end %>
       </div>
       <div class="md:hidden">
-        <button id="menu-button" class="text-gray-700 hover:text-gray-900 focus:outline-none" data-action="click->navigation#toggleMenu">
+        <button id="menu-button" class="text-gray-700 dark:text-slate-400 hover:text-gray-900 dark:hover:text-white focus:outline-none" data-action="click->navigation#toggleMenu">
           <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
           </svg>
         </button>
       </div>
     </div>
-    <div id="mobile-menu" class="absolute top-18 bg-slate-100 z-20 container mx-auto hidden md:hidden space-y-4 px-8 pb-4 shadow" data-navigation-target="mobileMenu">
-        <%= link_to 'Conferences', conferences_path, class: 'block hover:text-gray-900' %>
-        <%= link_to 'Assignments', assignments_path, class: 'block hover:text-gray-900' %>
+    <div id="mobile-menu" class="absolute top-18 bg-slate-100 dark:bg-zinc-700 z-20 container mx-auto hidden md:hidden space-y-4 px-8 pb-4 shadow" data-navigation-target="mobileMenu">
+        <%= link_to 'Conferences', conferences_path, class: 'block hover:text-gray-900 dark:hover:text-white' %>
+        <%= link_to 'Assignments', assignments_path, class: 'block hover:text-gray-900 dark:hover:text-white' %>
         <hr>
         <% if user_signed_in? %>
         <div>logged in as <%= render partial: 'application/user_avatar', locals: { user: current_user } %></div>
-        <%= link_to 'My Profile', edit_user_registration_path, class: 'block hover:text-gray-900' %>
-        <%= link_to 'My Assignments', user_assignments_path(current_user), class: 'block hover:text-gray-900' %>
-        <%= link_to 'Logout', destroy_user_session_path, data: { turbo_method: :delete }, class: 'block hover:text-gray-900' %>
+        <%= link_to 'My Profile', edit_user_registration_path, class: 'block hover:text-gray-900 dark:hover:text-white' %>
+        <%= link_to 'My Assignments', user_assignments_path(current_user), class: 'block hover:text-gray-900 dark:hover:text-white' %>
+        <%= link_to 'Logout', destroy_user_session_path, data: { turbo_method: :delete }, class: 'block hover:text-gray-900 dark:hover:text-white' %>
         <% else %>
         <div>not logged in</div>
-        <%= link_to 'Login', new_user_session_path, class: 'block hover:text-gray-900' %>
-        <%= link_to 'Sign Up', new_user_registration_path, class: 'block hover:text-gray-900' %>
+        <%= link_to 'Login', new_user_session_path, class: 'block hover:text-gray-900 dark:hover:text-white' %>
+        <%= link_to 'Sign Up', new_user_registration_path, class: 'block hover:text-gray-900 dark:hover:text-white  ' %>
         <% end %>
     </div>
   </nav>
diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
index 623dd04fd51a2fe2bb0c3e4f599a5416ce68e151..b0584be9fa1e13b670837562f9931db3db6a9464 100644
--- a/app/views/sessions/_session.html.erb
+++ b/app/views/sessions/_session.html.erb
@@ -1,6 +1,6 @@
 <% unassigned_users = User.all - session.assignments.collect(&:user) %>
 <%= turbo_frame_tag dom_id(session), method: "morph", class: "w-full", data: { controller: "session", language: session.language } do %>
-  <div class="session shadow hover:shadow-lg overflow-scroll text-sm w-full !h-full min-h-full hover:!min-h-max <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.assignees? ? "has-assignees" : "no-assignees" %> <%= (session.ends_at < Time.now ? "past" : "") %>">
+  <div class="session dark:text-black shadow hover:shadow-lg overflow-scroll text-sm w-full !h-full min-h-full hover:!min-h-max <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.assignees? ? "has-assignees" : "no-assignees" %> <%= (session.ends_at < Time.now ? "past" : "") %>">
     <h4>
       <small class="text-2xs uppercase font-bold <%= session.language=="de" ? "text-sky-700 border-sky-700" : "text-fuchsia-700 border-fuchsia-700" %> border bg-black/10 rounded-sm p-1 mr-1 lang-<%= session.language %>"><%= session.language %></small><% unless session.recorded %><span aria-label="Session is not recorded" title="Session is not recorded"><svg class="inline-block -mt-0.5 w-5 h-5" fill="#000000" version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m255-1c-141 0-256 115-256 256s115 256 256 256 256-115 256-256-115-256-256-256zm0 17c63 0 121 25 163 65l-65 65-19-48c-0.85-2.6-4.3-5.1-7.7-5.1h-142c-3.4 0-6 1.7-7.7 5.1l-21 55h-49c-17 0-31 14-31 31v169c0 17 14 31 31 31h6.7l-34 34c-39-43-64-100-64-162 0-131 108-239 239-239zm60 239c0 33-26 60-60 60-14 0-26-4.4-36-12l84-83c7.3 9.9 12 22 12 36zm-119 0c0-33 26-60 60-60 14 0 26 4.5 36 12l-84 83c-7.4-9.9-12-22-12-36zm108-60c-13-11-30-17-48-17-43 0-77 34-77 77 0 18 6.2 35 17 48l-64 63h-25c-7.7 0-14-6-14-14v-169c0-7.7 6-14 14-14h55c3.4 0 6.8-2.6 7.7-5.1l21-55h130l19 50-36 36zm-97 120c13 11 30 17 48 17 43 0 77-34 77-77 0-18-6.1-35-16-48l38-38h49c7.7 0 14 6 14 14v169c0 7.7-6 14-14 14h-247l51-51zm48 179c-63 0-120-25-163-65l46-46h265c17 0 31-14 32-31v-169c0-17-14-31-31-31h-34l60-59c39 43 64 100 64 162-1e-3 131-108 239-239 239z"/><path d="m383 187c-9.4 0-17 7.7-17 17s7.7 17 17 17 17-7.7 17-17-7.7-17-17-17z"/></svg></span><% end %><%= render partial: 'shared/session_filedrop', locals: { session: } %>
       <%= link_to session.title, session.url, target: "_blank" %>
diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb
index 39a2b539080bbdf53a0ad123bdb5962cec66db53..3ce9d3dd8f98f534d961bfe5db0521ed9b06d143 100644
--- a/app/views/sessions/show.html.erb
+++ b/app/views/sessions/show.html.erb
@@ -1,6 +1,6 @@
 <div>
   <h6><%= link_to @session.conference.name, @session.conference %></h6>
-  <h1>
+  <h1 class="dark:text-red-500">
     <%= @session.title %>
     <% unless @session.url.blank? %>
     <%= link_to "🔗 open in Fahrplan", @session.url, class: "ml-4 font-normal" %>
@@ -15,9 +15,9 @@
   <h3 class="mt-4">Comments <span class="font-normal">from Speakers' Filedrop</span></h2>
   <ul class="space-y-4 my-4">
     <% @session.filedrop_comments.each do |comment| %>
-    <li class="bg-gray-100 shadow rounded-lg p-2">
-      <div class="text-gray-900 whitespace-pre-wrap"><%= comment.body %></div>
-      <div class="text-gray-500 text-sm mt-2">
+    <li class="text-gray-900 dark:text-slate-200 bg-gray-100 dark:bg-gray-600 shadow rounded-lg p-2">
+      <div class="whitespace-pre-wrap"><%= comment.body %></div>
+      <div class="text-sm mt-2">
         <%= comment.orig_created&.in_time_zone(@session.conference.time_zone || 'UTC')&.strftime("%B %d, %Y %H:%M") %>
       </div>
     </li>
@@ -29,13 +29,13 @@
   <h3 class="mt-4">Files <span class="font-normal">from Speakers' Filedrop</span></h3>
   <ul class="space-y-4 my-4">
     <% @session.filedrop_files.each do |file| %>
-    <li class="bg-white shadow rounded-lg p-4">
+    <li class="text-gray-500 dark:text-slate-400 shadow dark:shadow-gray-500 rounded-lg p-4">
         <div class="flex flex-wrap justify-between items-start max-w-full">
           <div>
-            <div class="text-gray-900 text-lg font-semibold"><%= file.name %></div>
-            <div class="text-gray-500 text-sm">Size: <%= number_to_human_size(file.size) %></div>
-            <div class="text-gray-500 text-sm">Date: <%=file.orig_created&.in_time_zone(@session.conference.time_zone || 'UTC')&.strftime("%B %d, %Y %H:%M") %></div>
-            <div class="text-gray-500 text-sm">Checksum: <%= file.checksum %></div>
+            <div class="text-gray-900 dark:text-slate-300 text-lg font-semibold"><%= file.name %></div>
+            <div class="text-sm">Size: <%= number_to_human_size(file.size) %></div>
+            <div class="text-sm">Date: <%=file.orig_created&.in_time_zone(@session.conference.time_zone || 'UTC')&.strftime("%B %d, %Y %H:%M") %></div>
+            <div class="text-sm">Checksum: <%= file.checksum %></div>
           </div>
         <div class="mt-2 sm:mt-0 flex-shrink-0">
           <%= link_to 'Download', download_filedrop_file_path(file), class: "bg-blue-500 text-white px-3 py-2 rounded" %>
diff --git a/app/views/shared/_flash.html.erb b/app/views/shared/_flash.html.erb
index aff4cb59ee2cfe637e815914d48344ea6fba98e7..ef5b20ca0201cda70d3e07144c82b6dfda08c51b 100644
--- a/app/views/shared/_flash.html.erb
+++ b/app/views/shared/_flash.html.erb
@@ -1,4 +1,4 @@
-<div id="flash" class="relative top-0.5 md:top-16">
+<div id="flash" class="relative top-0.5 md:top-16 dark:text-black">
   <% flash.each do |type, message| %>
     <div class="flash alert alert-<%= type %>">
       <%= message %>
diff --git a/app/views/speakers/show.html.erb b/app/views/speakers/show.html.erb
index 5780d9ef422e010d6201f1c3b5cae3eecb3ddade..aa4a48497e6c726139a7651e27fad6d374eb4cee 100644
--- a/app/views/speakers/show.html.erb
+++ b/app/views/speakers/show.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h1 class="font-bold text-4xl"><%= @speaker.name %></h1>
+  <h1 class="font-bold text-4xl dark:text-red-500"><%= @speaker.name %></h1>
   <h2 class="font-medium text-2xl"><%= @speaker.position %></h2>
   <p><%= @speaker.description.html_safe %></p>
   <% # = link_to "#{@speaker.name} at #{@speaker.conference.name}", %>
diff --git a/app/views/users/leaderboard.html.erb b/app/views/users/leaderboard.html.erb
index fb22a0c21453eb21c290e475ac826bd3d53613c8..0bf5ae0ab64d953afc95d7522e081c57deccd1f8 100644
--- a/app/views/users/leaderboard.html.erb
+++ b/app/views/users/leaderboard.html.erb
@@ -1,5 +1,5 @@
 <div class="w-full">
-  <h1 class="text-xl my-4">Leaderboard</h1>
+  <h1 class="text-xl my-4 dark:text-red-500">Leaderboard</h1>
     <dl class="w-full max-w-4xl mx-auto">
       <% top_workload = @workload_data.values.max %>
       <% @workload_data.each_with_index do |(username, workload), index| %>
diff --git a/app/views/users/login.html.erb b/app/views/users/login.html.erb
index 7c99ca874b99aa6b93dd46f34928f750ed7d2554..6b50d7e65fdd53220e6d18e4b4d04253c507b199 100644
--- a/app/views/users/login.html.erb
+++ b/app/views/users/login.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h1 class="font-bold text-4xl mb-8">Choose User</h1>
+  <h1 class="font-bold text-4xl mb-8 dark:text-red-500">Choose User</h1>
   <ul class="flex flex-wrap gap-2">
   <% @users.each do |user| %>
     <li>
diff --git a/app/views/users/profile.html.erb b/app/views/users/profile.html.erb
index b79fdb86c2afaa8e3df9e7fd7153528a310986de..b58bff8f7a0c01e49bac23dfc83f68b89dacfd61 100644
--- a/app/views/users/profile.html.erb
+++ b/app/views/users/profile.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h1 class="font-bold text-4xl">Profile</h1>
+  <h1 class="font-bold text-4xl dark:text-red-500">Profile</h1>
   <%= form_with(model: @user, url: update_profile_path, local: true) do |form| %>
 
     <div class="field">
diff --git a/config/tailwind.config.js b/config/tailwind.config.js
index 06dd449652a9224fd86e90e08ccd7448d3bb7fde..71a0f8bfa4616b3b8b9358321f268b3cbb85f67c 100644
--- a/config/tailwind.config.js
+++ b/config/tailwind.config.js
@@ -7,6 +7,7 @@ module.exports = {
     './app/javascript/**/*.js',
     './app/views/**/*.{erb,haml,html,slim}'
   ],
+  darkMode: 'selector',
   theme: {
     extend: {
       keyframes: {
diff --git a/db/migrate/20250101154742_add_darkmode_to_users.rb b/db/migrate/20250101154742_add_darkmode_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..72aaf7eea91851f26e4cf8395131768e863126ed
--- /dev/null
+++ b/db/migrate/20250101154742_add_darkmode_to_users.rb
@@ -0,0 +1,5 @@
+class AddDarkmodeToUsers < ActiveRecord::Migration[7.1]
+  def change
+    add_column :users, :darkmode, :integer, null: false, default: 0
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 707b0988ada51f26d198e3b70667b2cb7ccd14d7..d8b064ff212cc0c9f7ee77bfb39ea8c6a0fb18ab 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.1].define(version: 2024_12_30_120900) do
+ActiveRecord::Schema[7.1].define(version: 2025_01_01_154742) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -307,6 +307,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_12_30_120900) do
     t.string "invitation_token"
     t.string "languages_from"
     t.string "languages_to"
+    t.integer "darkmode", default: 0, null: false
   end
 
   add_foreign_key "assignments", "sessions"