Skip to content

Commit afc0290

Browse files
committed
feat: Light theme
Add a new light theme and a switcher to toggle between it and the existing dark theme.
1 parent b0b85f0 commit afc0290

10 files changed

Lines changed: 316 additions & 44 deletions

File tree

assets/css/app.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,20 @@
1212
@apply bg-gray-500;
1313
border-radius: 4px;
1414
}
15+
16+
/* ===== Light Theme ===== */
17+
body.light-theme {
18+
background-color: rgb(255 255 255);
19+
color: rgb(17 24 39);
20+
}
21+
22+
/* Theme toggle icon visibility */
23+
.theme-icon-light {
24+
display: none;
25+
}
26+
body.light-theme .theme-icon-dark {
27+
display: none;
28+
}
29+
body.light-theme .theme-icon-light {
30+
display: block;
31+
}

assets/js/app.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@ let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("
55
let livePath = document.querySelector("meta[name='live-path']").getAttribute("content");
66
let liveTransport = document .querySelector("meta[name='live-transport']") .getAttribute("content");
77

8+
// Theme management
9+
const Theme = {
10+
STORAGE_KEY: "error-tracker-theme",
11+
12+
init() {
13+
const saved = localStorage.getItem(this.STORAGE_KEY);
14+
if (saved === "light") {
15+
document.body.classList.add("light-theme");
16+
}
17+
},
18+
19+
toggle() {
20+
const isLight = document.body.classList.toggle("light-theme");
21+
localStorage.setItem(this.STORAGE_KEY, isLight ? "light" : "dark");
22+
},
23+
24+
isLight() {
25+
return document.body.classList.contains("light-theme");
26+
}
27+
};
28+
829
const Hooks = {
930
JsonPrettyPrint: {
1031
mounted() {
@@ -26,6 +47,11 @@ const Hooks = {
2647
// Keep the original content if there's an error
2748
}
2849
}
50+
},
51+
ThemeInit: {
52+
mounted() {
53+
Theme.init();
54+
}
2955
}
3056
};
3157

@@ -41,6 +67,14 @@ topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" });
4167
window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300));
4268
window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide());
4369

70+
// Set up theme toggle via event delegation (CSP-compliant, avoids inline onclick)
71+
document.addEventListener("click", function(e) {
72+
var toggle = e.target.closest("[data-theme-toggle]");
73+
if (toggle) {
74+
Theme.toggle();
75+
}
76+
});
77+
4478
// connect if there are any LiveViews on the page
4579
liveSocket.connect();
4680
window.liveSocket = liveSocket;

assets/tailwind.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = {
1717
plugin(({addVariant}) => addVariant('phx-no-feedback', ['&.phx-no-feedback', '.phx-no-feedback &'])),
1818
plugin(({addVariant}) => addVariant('phx-click-loading', ['&.phx-click-loading', '.phx-click-loading &'])),
1919
plugin(({addVariant}) => addVariant('phx-submit-loading', ['&.phx-submit-loading', '.phx-submit-loading &'])),
20-
plugin(({addVariant}) => addVariant('phx-change-loading', ['&.phx-change-loading', '.phx-change-loading &']))
20+
plugin(({addVariant}) => addVariant('phx-change-loading', ['&.phx-change-loading', '.phx-change-loading &'])),
21+
plugin(({addVariant}) => addVariant('light', 'body.light-theme &'))
2122
]
2223
}

lib/error_tracker/web/components/core_components.ex

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule ErrorTracker.Web.CoreComponents do
2121
<.link
2222
class={[
2323
"phx-submit-loading:opacity-75 py-[11.5px]",
24-
"text-sm font-semibold text-sky-500 hover:text-white/80",
24+
"text-sm font-semibold text-sky-500 light:text-sky-600 hover:text-white/80 light:hover:text-gray-900/80",
2525
@class
2626
]}
2727
{@rest}
@@ -63,14 +63,14 @@ defmodule ErrorTracker.Web.CoreComponents do
6363
def badge(assigns) do
6464
color_class =
6565
case assigns.color do
66-
:blue -> "bg-blue-900 text-blue-300"
67-
:gray -> "bg-gray-700 text-gray-300"
68-
:red -> "bg-red-400/10 text-red-300 ring-red-400/20"
69-
:green -> "bg-emerald-400/10 text-emerald-300 ring-emerald-400/20"
70-
:yellow -> "bg-yellow-900 text-yellow-300"
71-
:indigo -> "bg-indigo-900 text-indigo-300"
72-
:purple -> "bg-purple-900 text-purple-300"
73-
:pink -> "bg-pink-900 text-pink-300"
66+
:blue -> "bg-blue-900 light:bg-blue-100 text-blue-300 light:text-blue-800"
67+
:gray -> "bg-gray-700 light:bg-gray-200 text-gray-300 light:text-gray-700"
68+
:red -> "bg-red-400/10 light:bg-red-100 text-red-300 light:text-red-800 ring-red-400/20 light:ring-red-400/30"
69+
:green -> "bg-emerald-400/10 light:bg-emerald-100 text-emerald-300 light:text-emerald-800 ring-emerald-400/20 light:ring-emerald-400/30"
70+
:yellow -> "bg-yellow-900 light:bg-yellow-100 text-yellow-300 light:text-yellow-800"
71+
:indigo -> "bg-indigo-900 light:bg-indigo-100 text-indigo-300 light:text-indigo-800"
72+
:purple -> "bg-purple-900 light:bg-purple-100 text-purple-300 light:text-purple-800"
73+
:pink -> "bg-pink-900 light:bg-pink-100 text-pink-300 light:text-pink-800"
7474
end
7575

7676
assigns = Map.put(assigns, :color_class, color_class)
@@ -95,14 +95,14 @@ defmodule ErrorTracker.Web.CoreComponents do
9595
<div class="mt-10 w-full flex">
9696
<button
9797
:if={@page > 1}
98-
class="flex items-center justify-center px-4 h-10 text-base font-medium text-gray-400 bg-gray-900 border border-gray-400 rounded-lg hover:bg-gray-800 hover:text-white"
98+
class="flex items-center justify-center px-4 h-10 text-base font-medium text-gray-400 light:text-gray-500 bg-gray-900 light:bg-gray-100 border border-gray-400 light:border-gray-300 rounded-lg hover:bg-gray-800 light:hover:bg-gray-200 hover:text-white light:hover:text-gray-900"
9999
phx-click={@event_previous}
100100
>
101101
Previous page
102102
</button>
103103
<button
104104
:if={@page < @total_pages}
105-
class="flex items-center justify-center px-4 h-10 text-base font-medium text-gray-400 bg-gray-900 border border-gray-400 rounded-lg hover:bg-gray-800 hover:text-white"
105+
class="flex items-center justify-center px-4 h-10 text-base font-medium text-gray-400 light:text-gray-500 bg-gray-900 light:bg-gray-100 border border-gray-400 light:border-gray-300 rounded-lg hover:bg-gray-800 light:hover:bg-gray-200 hover:text-white light:hover:text-gray-900"
106106
phx-click={@event_next}
107107
>
108108
Next page
@@ -122,7 +122,7 @@ defmodule ErrorTracker.Web.CoreComponents do
122122
<div>
123123
<h2
124124
:if={assigns[:title]}
125-
class={["text-sm font-semibold mb-2 uppercase text-gray-400", @title_class]}
125+
class={["text-sm font-semibold mb-2 uppercase text-gray-400 light:text-gray-500", @title_class]}
126126
>
127127
{@title}
128128
</h2>

lib/error_tracker/web/components/layouts.ex

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ defmodule ErrorTracker.Web.Layouts do
3535

3636
def navbar(assigns) do
3737
~H"""
38-
<nav class="border-gray-400 bg-gray-900">
38+
<nav class="border-gray-400 light:border-gray-300 bg-gray-900 light:bg-gray-100">
3939
<div class="container flex flex-wrap items-center justify-between mx-auto p-4">
4040
<.link
4141
href={dashboard_path(@socket)}
42-
class="self-center text-2xl font-semibold whitespace-nowrap text-white"
42+
class="self-center text-2xl font-semibold whitespace-nowrap text-white light:text-gray-900"
4343
>
4444
<span class="mr-2">🐛</span>ErrorTracker
4545
</.link>
4646
<button
4747
type="button"
48-
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded -lg md:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-500"
48+
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded -lg md:hidden focus:outline-none focus:ring-2 text-gray-400 light:text-gray-500 hover:bg-gray-700 light:hover:bg-gray-200 focus:ring-gray-500"
4949
aria-controls="navbar-main"
5050
aria-expanded="false"
5151
phx-click={JS.toggle(to: "#navbar-main")}
@@ -68,7 +68,7 @@ defmodule ErrorTracker.Web.Layouts do
6868
</svg>
6969
</button>
7070
<div class="hidden w-full md:block md:w-auto" id="navbar-main">
71-
<ul class="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-400 bg-gray-900 rounded-lg md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-gray-800">
71+
<ul class="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-400 light:border-gray-300 bg-gray-900 light:bg-gray-100 rounded-lg md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-gray-800 light:md:bg-gray-50">
7272
<.navbar_item to="https://github.com/elixir-error-tracker/error-tracker" target="_blank">
7373
<svg
7474
width="18"
@@ -86,6 +86,39 @@ defmodule ErrorTracker.Web.Layouts do
8686
</svg>
8787
GitHub
8888
</.navbar_item>
89+
<li>
90+
<button
91+
data-theme-toggle
92+
class="block py-2 px-3 rounded-lg text-white light:text-gray-900 hover:text-white light:hover:text-gray-900 hover:bg-gray-700 light:hover:bg-gray-200 md:hover:bg-transparent md:border-0 md:hover:text-sky-500"
93+
aria-label="Toggle theme"
94+
title="Toggle theme"
95+
>
96+
<!-- Moon icon (shown in dark mode) -->
97+
<svg
98+
xmlns="http://www.w3.org/2000/svg"
99+
viewBox="0 0 20 20"
100+
fill="currentColor"
101+
class="w-5 h-5 theme-icon-dark"
102+
>
103+
<path
104+
fill-rule="evenodd"
105+
d="M7.455 2.004a.75.75 0 0 1 .26.77 7 7 0 0 0 9.958 7.967.75.75 0 0 1 1.067.853A8.5 8.5 0 1 1 6.647 1.921a.75.75 0 0 1 .808.083Z"
106+
clip-rule="evenodd"
107+
/>
108+
</svg>
109+
<!-- Sun icon (shown in light mode) -->
110+
<svg
111+
xmlns="http://www.w3.org/2000/svg"
112+
viewBox="0 0 20 20"
113+
fill="currentColor"
114+
class="w-5 h-5 theme-icon-light"
115+
>
116+
<path
117+
d="M10 2a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 2ZM10 15a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 15ZM10 7a3 3 0 1 0 0 6 3 3 0 0 0 0-6ZM15.657 5.404a.75.75 0 1 0-1.06-1.06l-1.061 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM6.464 14.596a.75.75 0 1 0-1.06-1.06l-1.06 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM18 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 18 10ZM5 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 5 10ZM14.596 15.657a.75.75 0 0 0 1.06-1.06l-1.06-1.061a.75.75 0 1 0-1.06 1.06l1.06 1.06ZM5.404 6.464a.75.75 0 0 0 1.06-1.06l-1.06-1.06a.75.75 0 1 0-1.061 1.06l1.06 1.06Z"
118+
/>
119+
</svg>
120+
</button>
121+
</li>
89122
</ul>
90123
</div>
91124
</div>
@@ -103,7 +136,7 @@ defmodule ErrorTracker.Web.Layouts do
103136
<li>
104137
<a
105138
href={@to}
106-
class="whitespace-nowrap flex-0 block py-2 px-3 rounded-lg text-white hover:text-white hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-sky-500"
139+
class="whitespace-nowrap flex-0 block py-2 px-3 rounded-lg text-white light:text-gray-900 hover:text-white light:hover:text-gray-900 hover:bg-gray-700 light:hover:bg-gray-200 md:hover:bg-transparent md:border-0 md:hover:text-sky-500"
107140
{@rest}
108141
>
109142
{render_slot(@inner_block)}

lib/error_tracker/web/components/layouts/root.html.heex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
</script>
2626
</head>
2727

28-
<body class="bg-gray-800 text-white">
28+
<body id="body" class="bg-gray-800 text-white" phx-hook="ThemeInit">
2929
{@inner_content}
3030
</body>
3131
</html>

lib/error_tracker/web/live/dashboard.html.heex

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@
99
value={@search_form[:reason].value}
1010
type="text"
1111
placeholder="Error"
12-
class="border text-sm rounded-lg block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
12+
class="border text-sm rounded-lg block p-2.5 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300 placeholder-gray-400 text-white light:text-gray-900 focus:ring-blue-500 focus:border-blue-500"
1313
phx-debounce
1414
/>
1515
<input
1616
name={@search_form[:source_line].name}
1717
value={@search_form[:source_line].value}
1818
type="text"
1919
placeholder="Source line"
20-
class="border text-sm rounded-lg block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
20+
class="border text-sm rounded-lg block p-2.5 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300 placeholder-gray-400 text-white light:text-gray-900 focus:ring-blue-500 focus:border-blue-500"
2121
phx-debounce
2222
/>
2323
<input
2424
name={@search_form[:source_function].name}
2525
value={@search_form[:source_function].value}
2626
type="text"
2727
placeholder="Source function"
28-
class="border text-sm rounded-lg block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
28+
class="border text-sm rounded-lg block p-2.5 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300 placeholder-gray-400 text-white light:text-gray-900 focus:ring-blue-500 focus:border-blue-500"
2929
phx-debounce
3030
/>
3131
<select
3232
name={@search_form[:status].name}
33-
class="border text-sm rounded-lg block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
33+
class="border text-sm rounded-lg block p-2.5 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300 placeholder-gray-400 text-white light:text-gray-900 focus:ring-blue-500 focus:border-blue-500"
3434
>
3535
<option value="" selected={@search_form[:status].value == ""}>All</option>
3636
<option value="unresolved" selected={@search_form[:status].value == "unresolved"}>
@@ -42,9 +42,9 @@
4242
</select>
4343
</.form>
4444

45-
<div class="relative overflow-x-auto shadow-md sm:rounded-lg ring-1 ring-gray-900">
46-
<table class="w-full text-sm text-left rtl:text-right text-gray-400 table-fixed">
47-
<thead class="text-xs uppercase bg-gray-900">
45+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg ring-1 ring-gray-900 light:ring-gray-200">
46+
<table class="w-full text-sm text-left rtl:text-right text-gray-400 light:text-gray-500 table-fixed">
47+
<thead class="text-xs uppercase bg-gray-900 light:bg-gray-100">
4848
<tr>
4949
<th scope="col" class="px-4 pr-2 w-72">Error</th>
5050
<th scope="col" class="px-4 py-3 w-72">Occurrences</th>
@@ -60,9 +60,9 @@
6060
</tr>
6161
<tr
6262
:for={error <- @errors}
63-
class="border-b bg-gray-400/10 border-y border-gray-900 hover:bg-gray-800/60 last-of-type:border-b-0"
63+
class="border-b bg-gray-400/10 light:bg-gray-50 border-y border-gray-900 light:border-gray-200 hover:bg-gray-800/60 light:hover:bg-gray-100 last-of-type:border-b-0"
6464
>
65-
<td scope="row" class="px-4 py-4 font-medium text-white relative">
65+
<td scope="row" class="px-4 py-4 font-medium text-white light:text-gray-900 relative">
6666
<.link navigate={error_path(@socket, error, @search)} class="absolute inset-1">
6767
<span class="sr-only">({sanitize_module(error.kind)}) {error.reason}</span>
6868
</.link>
@@ -71,7 +71,7 @@
7171
</p>
7272
<p
7373
:if={ErrorTracker.Error.has_source_info?(error)}
74-
class="whitespace-nowrap text-ellipsis overflow-hidden font-normal text-gray-400"
74+
class="whitespace-nowrap text-ellipsis overflow-hidden font-normal text-gray-400 light:text-gray-500"
7575
>
7676
{sanitize_module(error.source_function)}
7777
<br />

lib/error_tracker/web/live/show.html.heex

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</div>
66

77
<div id="header">
8-
<p class="text-sm uppercase font-semibold text-gray-400">
8+
<p class="text-sm uppercase font-semibold text-gray-400 light:text-gray-500">
99
Error #{@error.id} @ {format_datetime(@occurrence.inserted_at)}
1010
</p>
1111
<h1 class="my-1 text-2xl w-full font-semibold whitespace-nowrap text-ellipsis overflow-hidden">
@@ -16,44 +16,44 @@
1616
</div>
1717

1818
<div class="grid grid-cols-1 md:grid-cols-4 md:space-x-3 mt-6 gap-2">
19-
<div class="md:col-span-3 md:border-r md:border-gray-600 space-y-8 pr-5">
19+
<div class="md:col-span-3 md:border-r md:border-gray-600 light:md:border-gray-300 space-y-8 pr-5">
2020
<.section title="Full message">
21-
<pre class="overflow-auto p-4 rounded-lg bg-gray-300/10 border border-gray-900"><%= @occurrence.reason %></pre>
21+
<pre class="overflow-auto p-4 rounded-lg bg-gray-300/10 light:bg-gray-100 border border-gray-900 light:border-gray-200"><%= @occurrence.reason %></pre>
2222
</.section>
2323

2424
<.section :if={ErrorTracker.Error.has_source_info?(@error)} title="Source">
25-
<pre class="overflow-auto text-sm p-4 rounded-lg bg-gray-300/10 border border-gray-900">
25+
<pre class="overflow-auto text-sm p-4 rounded-lg bg-gray-300/10 light:bg-gray-100 border border-gray-900 light:border-gray-200">
2626
<%= sanitize_module(@error.source_function) %>
2727
<%= @error.source_line %></pre>
2828
</.section>
2929

3030
<.section :if={@occurrence.breadcrumbs != []} title="Bread crumbs">
31-
<div class="relative overflow-x-auto shadow-md sm:rounded-lg ring-1 ring-gray-900">
32-
<table class="w-full text-sm text-gray-400 table-fixed">
31+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg ring-1 ring-gray-900 light:ring-gray-200">
32+
<table class="w-full text-sm text-gray-400 light:text-gray-500 table-fixed">
3333
<tr
3434
:for={
3535
{breadcrumb, index} <-
3636
@occurrence.breadcrumbs |> Enum.reverse() |> Enum.with_index()
3737
}
38-
class="border-b bg-gray-400/10 border-gray-900 last:border-b-0"
38+
class="border-b bg-gray-400/10 light:bg-gray-50 border-gray-900 light:border-gray-200 last:border-b-0"
3939
>
40-
<td class="w-11 pl-2 py-4 font-medium text-white relative text-right">
40+
<td class="w-11 pl-2 py-4 font-medium text-white light:text-gray-900 relative text-right">
4141
{length(@occurrence.breadcrumbs) - index}.
4242
</td>
43-
<td class="px-2 py-4 font-medium text-white relative">{breadcrumb}</td>
43+
<td class="px-2 py-4 font-medium text-white light:text-gray-900 relative">{breadcrumb}</td>
4444
</tr>
4545
</table>
4646
</div>
4747
</.section>
4848

4949
<.section :if={@occurrence.stacktrace.lines != []} title="Stacktrace">
50-
<div class="p-4 bg-gray-300/10 border border-gray-900 rounded-lg">
50+
<div class="p-4 bg-gray-300/10 light:bg-gray-100 border border-gray-900 light:border-gray-200 rounded-lg">
5151
<div class="w-full mb-4">
5252
<label class="flex justify-end">
5353
<input
5454
type="checkbox"
5555
id="show-app-frames"
56-
class="ml-2 mr-2 mb-1 mt-1 inline-block text-sky-600 rounded focus:ring-sky-600 ring-offset-gray-800 focus:ring-2 bg-gray-700 border-gray-600"
56+
class="ml-2 mr-2 mb-1 mt-1 inline-block text-sky-600 rounded focus:ring-sky-600 ring-offset-gray-800 light:ring-offset-white focus:ring-2 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300"
5757
phx-click={JS.toggle(to: "#stacktrace tr:not([data-app=#{@app}])")}
5858
/>
5959
<span class="text-md inline-block">
@@ -81,20 +81,20 @@
8181
<.section title="Context">
8282
<pre
8383
id="context"
84-
class="overflow-auto text-sm p-4 rounded-lg bg-gray-300/10 border border-gray-900"
84+
class="overflow-auto text-sm p-4 rounded-lg bg-gray-300/10 light:bg-gray-100 border border-gray-900 light:border-gray-200"
8585
phx-hook="JsonPrettyPrint"
8686
>
8787
<%= ErrorTracker.__default_json_encoder__().encode_to_iodata!(@occurrence.context) %>
8888
</pre>
8989
</.section>
9090
</div>
9191

92-
<div class="px-3 md:pl-0 space-y-8">
92+
<div class="px-3 md:pl-0 space-y-8 light:md:border-gray-300">
9393
<.section title={"Occurrence (#{@total_occurrences} total)"}>
9494
<form phx-change="occurrence_navigation">
9595
<select
9696
name="occurrence_id"
97-
class="w-full border text-sm rounded-lg block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
97+
class="w-full border text-sm rounded-lg block p-2.5 bg-gray-700 light:bg-white border-gray-600 light:border-gray-300 placeholder-gray-400 text-white light:text-gray-900 focus:ring-blue-500 focus:border-blue-500"
9898
>
9999
<option
100100
:for={occurrence <- @occurrences}

0 commit comments

Comments
 (0)