diff --git a/frontend/src/crm/customers/CustomerList.jsx b/frontend/src/crm/customers/CustomerList.jsx
index 05f322a..95d690f 100644
--- a/frontend/src/crm/customers/CustomerList.jsx
+++ b/frontend/src/crm/customers/CustomerList.jsx
@@ -839,13 +839,14 @@ export default function CustomerList() {
const isLast = index === pagedCustomers.length - 1;
const gradient = rowGradient(c, direction);
const rowBg = hoveredRow === c.id ? "var(--bg-card-hover)" : undefined;
+ const zebraBase = index % 2 === 1 ? "var(--bg-row-alt)" : "transparent";
const rowStyle = {
borderBottom: (!isLast && !(notesMode === "expanded" && hasStatus))
? "1px solid var(--border-secondary)"
: "none",
background: rowBg
? rowBg
- : gradient || "transparent",
+ : gradient || zebraBase,
};
const mainRow = (
diff --git a/frontend/src/crm/orders/OrderList.jsx b/frontend/src/crm/orders/OrderList.jsx
index b6556ae..bb8033f 100644
--- a/frontend/src/crm/orders/OrderList.jsx
+++ b/frontend/src/crm/orders/OrderList.jsx
@@ -163,7 +163,7 @@ export default function OrderList() {
className="cursor-pointer"
style={{
borderBottom: index < orders.length - 1 ? "1px solid var(--border-secondary)" : "none",
- backgroundColor: hoveredRow === o.id ? "var(--bg-card-hover)" : "transparent",
+ backgroundColor: hoveredRow === o.id ? "var(--bg-card-hover)" : index % 2 === 1 ? "var(--bg-row-alt)" : "transparent",
}}
onMouseEnter={() => setHoveredRow(o.id)}
onMouseLeave={() => setHoveredRow(null)}
diff --git a/frontend/src/crm/products/ProductList.jsx b/frontend/src/crm/products/ProductList.jsx
index 3eae720..4e4b2dc 100644
--- a/frontend/src/crm/products/ProductList.jsx
+++ b/frontend/src/crm/products/ProductList.jsx
@@ -150,7 +150,7 @@ export default function ProductList() {
className="cursor-pointer"
style={{
borderBottom: index < filtered.length - 1 ? "1px solid var(--border-primary)" : "none",
- backgroundColor: hoveredRow === p.id ? "var(--bg-card-hover)" : "transparent",
+ backgroundColor: hoveredRow === p.id ? "var(--bg-card-hover)" : index % 2 === 1 ? "var(--bg-row-alt)" : "transparent",
}}
onMouseEnter={() => setHoveredRow(p.id)}
onMouseLeave={() => setHoveredRow(null)}
diff --git a/frontend/src/crm/quotations/AllQuotationsList.jsx b/frontend/src/crm/quotations/AllQuotationsList.jsx
index 974614c..04f54bf 100644
--- a/frontend/src/crm/quotations/AllQuotationsList.jsx
+++ b/frontend/src/crm/quotations/AllQuotationsList.jsx
@@ -234,7 +234,7 @@ export default function AllQuotationsList() {
// Grid columns: thumbnail | number | customer | title | date | status | total
const GRID = "90px 120px 180px minmax(0,1fr) 110px 120px 130px";
- function renderRow(q) {
+ function renderRow(q, index = 0) {
const isDeleting = deleting === q.id;
const displayDate = q.is_legacy ? (q.legacy_date || q.created_at?.slice(0, 10) || "—") : (q.created_at?.slice(0, 10) || "—");
@@ -251,12 +251,12 @@ export default function AllQuotationsList() {
borderBottom: "1px solid var(--border-secondary)",
alignItems: "center",
minHeight: 110,
- backgroundColor: "var(--bg-card)",
+ backgroundColor: index % 2 === 1 ? "color-mix(in srgb, var(--bg-card) 97%, white)" : "var(--bg-card)",
cursor: "pointer",
}}
onClick={() => navigate(`/crm/customers/${q.customer_id}?tab=Quotations`)}
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--bg-card-hover)"}
- onMouseLeave={e => e.currentTarget.style.backgroundColor = "var(--bg-card)"}
+ onMouseLeave={e => e.currentTarget.style.backgroundColor = index % 2 === 1 ? "color-mix(in srgb, var(--bg-card) 97%, white)" : "var(--bg-card)"}
>
{/* Thumbnail — stops row click */}
e.stopPropagation()}>
@@ -406,7 +406,7 @@ export default function AllQuotationsList() {
))}
- {quotations.map(q => renderRow(q))}
+ {quotations.map((q, i) => renderRow(q, i))}
)}
diff --git a/frontend/src/crm/quotations/QuotationList.jsx b/frontend/src/crm/quotations/QuotationList.jsx
index dbb6c31..37e565c 100644
--- a/frontend/src/crm/quotations/QuotationList.jsx
+++ b/frontend/src/crm/quotations/QuotationList.jsx
@@ -426,7 +426,7 @@ export default function QuotationList({ customerId, onSend }) {
// Delete button is a hover-only overlay — no dedicated column needed
const GRID = "90px 120px minmax(0,1fr) 130px 130px 130px 90px";
- function renderRow(q, isLegacy = false) {
+ function renderRow(q, isLegacy = false, index = 0) {
const hasPdf = isLegacy ? !!q.legacy_pdf_path : !!q.nextcloud_pdf_url;
const displayDate = isLegacy ? (q.legacy_date || fmtDate(q.created_at)) : fmtDate(q.created_at);
const isDeleting = deleting === q.id;
@@ -444,10 +444,10 @@ export default function QuotationList({ customerId, onSend }) {
borderBottom: "1px solid var(--border-secondary)",
alignItems: "center",
minHeight: 110,
- backgroundColor: "var(--bg-card)",
+ backgroundColor: index % 2 === 1 ? "color-mix(in srgb, var(--bg-card) 97%, white)" : "var(--bg-card)",
}}
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--bg-card-hover)"}
- onMouseLeave={e => e.currentTarget.style.backgroundColor = "var(--bg-card)"}
+ onMouseLeave={e => e.currentTarget.style.backgroundColor = index % 2 === 1 ? "color-mix(in srgb, var(--bg-card) 97%, white)" : "var(--bg-card)"}
>
{/* Thumbnail */}
@@ -659,7 +659,7 @@ export default function QuotationList({ customerId, onSend }) {
{!loading && regular.length > 0 && (
0 ? 24 : 0 }}>
- {regular.map(q => renderRow(q, false))}
+ {regular.map((q, i) => renderRow(q, false, i))}
)}
@@ -681,7 +681,7 @@ export default function QuotationList({ customerId, onSend }) {
- {legacy.map(q => renderRow(q, true))}
+ {legacy.map((q, i) => renderRow(q, true, i))}
)}
diff --git a/frontend/src/devices/DeviceList.jsx b/frontend/src/devices/DeviceList.jsx
index 0eab768..39bd60d 100644
--- a/frontend/src/devices/DeviceList.jsx
+++ b/frontend/src/devices/DeviceList.jsx
@@ -465,14 +465,14 @@ export default function DeviceList() {
- {pagedDevices.map((device) => (
+ {pagedDevices.map((device, index) => (
navigate(`/devices/${device.id}`)}
className="cursor-pointer transition-colors"
- style={{ borderBottom: "1px solid var(--border-secondary)" }}
+ style={{ borderBottom: "1px solid var(--border-secondary)", backgroundColor: index % 2 === 1 ? "var(--bg-row-alt)" : "transparent" }}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = "var(--bg-card-hover)")}
- onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = "transparent")}
+ onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = index % 2 === 1 ? "var(--bg-row-alt)" : "transparent")}
>
{activeColumns.map((col) => (
| setHoveredRow(note.id)}
@@ -282,7 +282,7 @@ export default function NoteList() {
key={msg.id}
style={{
borderBottom: index < filtered.length - 1 ? "1px solid var(--border-primary)" : "none",
- backgroundColor: hoveredRow === msg.id ? "var(--bg-card-hover)" : "transparent",
+ backgroundColor: hoveredRow === msg.id ? "var(--bg-card-hover)" : index % 2 === 1 ? "var(--bg-row-alt)" : "transparent",
opacity: msg.acknowledged ? 0.6 : 1,
}}
onMouseEnter={() => setHoveredRow(msg.id)}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 3a678cc..34f3014 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -9,6 +9,7 @@
--bg-input: #111827;
--bg-sidebar: #111827;
--bg-header: #1f2937;
+ --bg-row-alt: rgba(255, 255, 255, 0.03);
--border-primary: #374151;
--border-secondary: #2d3748;
diff --git a/frontend/src/melodies/MelodyList.jsx b/frontend/src/melodies/MelodyList.jsx
index 92779ff..a6a98db 100644
--- a/frontend/src/melodies/MelodyList.jsx
+++ b/frontend/src/melodies/MelodyList.jsx
@@ -1300,14 +1300,14 @@ export default function MelodyList() {
|
- {pagedRows.map((row) => (
+ {pagedRows.map((row, index) => (
navigate(`/melodies/${row.id}`)}
className="cursor-pointer transition-colors"
- style={{ borderBottom: "1px solid var(--border-secondary)" }}
+ style={{ borderBottom: "1px solid var(--border-secondary)", backgroundColor: index % 2 === 1 ? "var(--bg-row-alt)" : "transparent" }}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = "var(--bg-card-hover)")}
- onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = "transparent")}
+ onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = index % 2 === 1 ? "var(--bg-row-alt)" : "transparent")}
>
{activeColumns.map((col) => (
|
|
- {archetypes.map((m) => {
+ {archetypes.map((m, index) => {
const assignedCount = getVerifiedAssignedCount(m);
return (
navigate(`/melodies/archetypes/${m.id}`)}
className="border-b cursor-pointer transition-colors hover:bg-[var(--bg-card-hover)]"
- style={{ borderColor: "var(--border-primary)" }}
+ style={{ borderColor: "var(--border-primary)", backgroundColor: index % 2 === 1 ? "var(--bg-row-alt)" : "transparent" }}
>
|
{m.name}
diff --git a/frontend/src/settings/StaffList.jsx b/frontend/src/settings/StaffList.jsx
index b625960..2e510fa 100644
--- a/frontend/src/settings/StaffList.jsx
+++ b/frontend/src/settings/StaffList.jsx
@@ -141,14 +141,14 @@ export default function StaffList() {
|
- {staff.map((member) => (
+ {staff.map((member, index) => (
navigate(`/settings/staff/${member.id}`)}
className="cursor-pointer transition-colors"
- style={{ borderBottom: "1px solid var(--border-secondary)" }}
+ style={{ borderBottom: "1px solid var(--border-secondary)", backgroundColor: index % 2 === 1 ? "var(--bg-row-alt)" : "transparent" }}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = "var(--bg-card-hover)")}
- onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = "transparent")}
+ onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = index % 2 === 1 ? "var(--bg-row-alt)" : "transparent")}
>
|
{member.name}
diff --git a/frontend/src/users/UserList.jsx b/frontend/src/users/UserList.jsx
index d923522..b895724 100644
--- a/frontend/src/users/UserList.jsx
+++ b/frontend/src/users/UserList.jsx
@@ -156,7 +156,7 @@ export default function UserList() {
className="cursor-pointer"
style={{
borderBottom: index < users.length - 1 ? "1px solid var(--border-primary)" : "none",
- backgroundColor: hoveredRow === user.id ? "var(--bg-card-hover)" : "transparent",
+ backgroundColor: hoveredRow === user.id ? "var(--bg-card-hover)" : index % 2 === 1 ? "var(--bg-row-alt)" : "transparent",
}}
onMouseEnter={() => setHoveredRow(user.id)}
onMouseLeave={() => setHoveredRow(null)}
|